Accessibility: WCAG 2.2, Section 508, accessible-by-default patterns
A comprehensive installation and audit reference for accessibility as a ranking and citation discipline. In 2026 accessibility is no longer a compliance afterthought. It is a measurable ranking…
WCAG 2.2 Conformance, WCAG 3.0 Preview, Semantic HTML for AI Parsing, Screen Reader Optimization, Keyboard Navigation, ARIA Implementation, Legal Exposure under ADA/EAA/AODA/Section 508, and the 2026 Convergence of Accessibility as a Measurable SEO and AI Citation Signal
A comprehensive installation and audit reference for accessibility as a ranking and citation discipline. In 2026 accessibility is no longer a compliance afterthought. It is a measurable ranking factor and a strong AI citation signal because synthesis engines preferentially extract from well structured semantic markup. The same heading hierarchy, landmark roles, ARIA labels, and semantic elements that screen readers consume are what AI extraction layers parse. An accessible site is an AI citable site.
Cross stack implementation note. The code samples in this framework are written in plain HTML for clarity. For React, Vue, Svelte, Next.js, Nuxt, SvelteKit, Astro, Hugo, 11ty, Remix, WordPress, Shopify, and Webflow equivalents see framework-cross-stack-implementation.md. For pure client rendered SPAs see framework-react.md. For Tailwind specific concerns see framework-tailwind.md.
1. Document Purpose
Accessibility in 2026 produces three compounding effects on a web property:
- Compliance protection. Reduces ADA Title III, European Accessibility Act, AODA, Section 508, and California Unruh Act exposure. Seyfarth Shaw 2025 federal lawsuit report counted 4,605 federal ADA Title III website filings in calendar year 2024. Settlements typically run 8,000 to 75,000 USD per case.
- Search and assistive technology overlap. Screen readers and Googlebot both parse the same semantic markup. A document outline a screen reader can navigate is a document outline Googlebot can index for ranking and Gemini can extract for AI Overview citation.
- Citation lift on AI surfaces. Surfer SEO October 2025 study of 41,200 URLs found pages with explicit ARIA landmark roles received 23 percent more AI Overview citations than topically equivalent pages with
<div>based scaffolding. Princeton GEO study (SIGKDD 2024) attributed similar lift to clean heading hierarchies.
Three independent dimensions converge:
- Semantic markup that AI engines parse. Reading mode bots (GPTBot, ClaudeBot, Google-Extended, PerplexityBot, OAI-SearchBot) parse first byte HTML without executing JavaScript. Semantic landmarks signal extractable structure.
- Structured content that screen readers and AI summarizers both consume. Lede paragraphs, H2 answer patterns, FAQPage mirrors, comparison tables, ordered lists. Identical lift on assistive technology comprehension and AI citation.
- Legal exposure under ADA Title III, the European Accessibility Act (full effect since June 28, 2025), Section 508, Ontario AODA, and California Unruh Act.
1.1 Three Operating Modes
Mode A, Install. Bring a new build to WCAG 2.2 AA conformance during development. Mode B, Audit. Evaluate an existing site for conformance and legal exposure. Mode C, Remediation. Audit first, then install for failing items in priority order.
1.2 Required Tools
- axe DevTools Chrome and Firefox extensions, free tier covers most WCAG 2.2 checks
- WAVE browser extension and
wave.webaim.org - Lighthouse Chrome DevTools accessibility category
- NVDA free Windows screen reader, VoiceOver built into macOS and iOS, JAWS paid Windows screen reader
- WebAIM Contrast Checker for WCAG AA and AAA contrast verification
- axe-core CLI, Pa11y CI, Lighthouse CI for headless CI integration on Bubbles at
/home/user/a11y/ - HeadingsMap Chrome extension to visualize heading hierarchy
- Accessibility Insights for Web Microsoft fast pass tool
1.3 Relationship to Neighboring Frameworks
The broader UX layer lives in framework-uxseo.md. Performance and Core Web Vitals in framework-pageexperience.md. Mobile concerns in framework-mobileseo.md. Schema graph patterns in framework-schema.md. E-E-A-T credibility in framework-eeat.md. AI citation mechanics in framework-aicitations.md.
2. Client Variables Intake
# ACCESSIBILITY FRAMEWORK CLIENT VARIABLES
# --- Business and Site Identity (REQUIRED) ---
business_name: ""
primary_domain: ""
jurisdiction_primary: "" # "us_only", "us_plus_eu", "us_plus_canada", "global"
public_sector_or_federal_contractor: false
ymyl_classification: "" # "full_ymyl", "partial_ymyl", "lite_ymyl", "non_ymyl"
# --- Conformance Target (REQUIRED) ---
wcag_target_level: "AA" # "A", "AA", "AAA"
wcag_version: "2.2"
accessibility_statement_published: false
last_independent_audit_date: ""
# --- Current State (REQUIRED) ---
lighthouse_accessibility_score: 0
axe_violations_critical: 0
axe_violations_serious: 0
wave_errors: 0
pa11y_ci_pass: false
# --- Semantic Markup Posture (REQUIRED) ---
semantic_landmarks_present: false
single_h1_per_page: false
heading_hierarchy_sequential: false
skip_link_present: false
focus_visible_implemented: false
# --- Form Accessibility (REQUIRED if forms present) ---
all_inputs_have_labels: false
autocomplete_attributes_present: false
fieldset_legend_for_groups: false
error_messages_associated: false
# --- Color and Contrast (REQUIRED) ---
text_contrast_4_5_to_1_minimum: false
ui_component_contrast_3_to_1: false
# --- Motion (REQUIRED) ---
prefers_reduced_motion_respected: false
no_autoplay_video_with_audio: false
no_flashing_above_3hz: false
# --- Testing Cadence (REQUIRED) ---
automated_ci_on_every_deploy: false
manual_keyboard_test_cadence: ""
manual_screen_reader_test_cadence: ""
Sites with public_sector_or_federal_contractor: true route to Section 508 (which references WCAG 2.0 AA technical baseline with agency procurement language uplifting toward 2.2). Sites with EU jurisdiction route to EAA conformance, harmonized via EN 301 549.
3. The 2026 Legal Landscape
3.1 ADA Title III in the United States
Federal courts have applied ADA Title III to commercial websites since the early 2010s. The Seyfarth Shaw 2025 federal accessibility lawsuit report counted 4,605 federal ADA Title III website filings in calendar year 2024 across an estimated 1,800 unique law firms. New York Southern District, New York Eastern District, and Florida Southern District remain the most active jurisdictions. California state court Unruh Act filings add a parallel volume.
Settlement economics in 2026:
| Case profile | Typical settlement range |
|---|---|
| First filing, small business, remediation commitment | 5,000 to 15,000 USD |
| First filing, mid market, complex remediation | 15,000 to 50,000 USD |
| Repeat defendant or enterprise tier | 50,000 to 250,000 USD |
| Class action or DOJ enforcement | 500,000 USD and above |
Courts most commonly reference WCAG 2.1 AA. WCAG 2.2 was published as W3C recommendation October 2023 and is now widely accepted as the operational target for new builds.
3.2 European Accessibility Act, June 2025
The European Accessibility Act (Directive 2019/882) took full effect across EU member states on June 28, 2025. Scope covers e-commerce, banking, e-books, ticketing, transport, telecommunications, computing hardware, ATMs, and consumer facing digital products. Harmonized technical standard EN 301 549 references WCAG 2.1 AA. US sites selling to EU consumers above the de minimis micro enterprise threshold must conform.
3.3 Section 508 Federal Requirement
Section 508 requires federal agencies and contractors to ensure ICT accessibility. Revised 508 Standards (effective January 18, 2018) reference WCAG 2.0 AA. Agency procurement language increasingly specifies WCAG 2.2 AA. Federal contractors failing VPAT or ACR review face procurement disqualification.
3.4 Ontario AODA
Web accessibility requirements binding on private sector organizations of 50 plus employees since January 1, 2021 (WCAG 2.0 AA). Compliance reporting continues through 2026. Out of scope for organizations with no Ontario nexus.
3.5 California Unruh Civil Rights Act
The Unruh Act extends ADA Title III protections to in state businesses regardless of physical presence. Statutory damages of 4,000 USD per violation plus attorney fees. Sites with substantial California traffic should treat WCAG 2.2 AA as the operational target.
3.6 Defense Posture
Three artifacts materially shift settlement leverage:
- Published accessibility statement at
/accessibility/listing target conformance level, accessibility contact, most recent audit date, and remediation roadmap. - Independent third party audit dated within the prior 12 months, performed by CPACC or WAS certified auditor, findings remediated or in documented remediation.
- Active remediation cadence with documented commits, deploy logs, and ticket queue showing ongoing work.
4. WCAG 2.2 Coverage
WCAG 2.2 published October 5, 2023 is the current W3C recommendation. Adds nine new success criteria to WCAG 2.1, removes one (4.1.1 Parsing), clarifies several existing criteria. Conformance levels A, AA, AAA stack. Target is AA.
4.1 The Four POUR Principles
- Perceivable. Text alternatives, time based media alternatives, content adaptable, distinguishable foreground from background.
- Operable. Keyboard accessible, enough time, no seizure triggers, navigable.
- Understandable. Readable, predictable, input assistance.
- Robust. Compatible with current and future user agents including assistive technology.
4.2 Conformance Levels
| Level | Description | Target |
|---|---|---|
| A | Minimum. A site failing A is functionally unusable for some user classes. | Required floor. |
| AA | Standard. The level most courts, regulators, and procurement reference. | Yes, operational target. |
| AAA | Enhanced. Higher contrast, exhaustive captioning, sign language, no time limits. | Aspirational where reasonable. |
4.3 The New Criteria in WCAG 2.2
Nine criteria added in 2.2, plus four carried from 2.1 that remain operationally relevant:
- 2.4.11 Focus Not Obscured (Minimum) AA. Focused element not entirely hidden by author content.
- 2.4.12 Focus Not Obscured (Enhanced) AAA. No part of focused element hidden.
- 2.4.13 Focus Appearance AAA. Focus indicator meets minimum size and contrast.
- 2.5.7 Dragging Movements AA. Dragging functionality available via single pointer alternative.
- 2.5.8 Target Size (Minimum) AA. Pointer targets at least 24 by 24 CSS pixels. Framework still recommends 44x44 mobile UX target for primary targets.
- 3.2.6 Consistent Help A. Help mechanisms appear in same relative order across pages.
- 3.3.7 Redundant Entry A. Previously entered information auto populated or selectable.
- 3.3.8 Accessible Authentication (Minimum) AA. Authentication does not require cognitive function test unless alternative available.
- 3.3.9 Accessible Authentication (Enhanced) AAA. No cognitive function test at all.
Carried from 2.1:
- 1.3.4 Orientation AA. Content does not restrict view to single orientation.
- 1.3.5 Identify Input Purpose AA. Input purpose programmatically determined via autocomplete.
- 1.4.10 Reflow AA. Content displays at 320 CSS pixel width without two dimensional scrolling.
- 1.4.11 Non text Contrast AA. UI components and graphical objects at least 3 to 1 contrast.
4.4 The Removed Criterion: 4.1.1 Parsing
WCAG 2.2 marks 4.1.1 Parsing obsolete. Modern parsers handle malformed HTML reliably. Still write well formed HTML and avoid duplicate IDs (they break label-for pairing and ARIA references), but parsing is no longer a separate audit point.
4.5 Critical Criteria
A site failing any of these eight functionally fails both WCAG 2.2 AA and AI citation eligibility on substantial query classes:
1.1.1 Non-text Content, 1.3.1 Info and Relationships, 1.3.5 Identify Input Purpose, 1.4.3 Contrast Minimum, 2.1.1 Keyboard, 2.4.6 Headings and Labels, 2.4.7 Focus Visible, 3.3.2 Labels or Instructions.
5. WCAG 3.0 Preview
W3C published WCAG 3.0 working draft updates through 2024 and 2025. Latest public working draft dated July 2024 with subsequent editor drafts through Q1 2026. Not yet a published recommendation. Major shifts:
5.1 Scoring Model
WCAG 2.x conformance is binary per success criterion. WCAG 3.0 proposes a 0 to 100 page level score combining weighted outcomes. Pages can be bronze, silver, or gold conformant. Bronze maps approximately to WCAG 2.2 AA.
5.2 Expanded Scope
Working draft expands to native mobile apps, immersive XR, voice user interfaces, and conversational agents.
5.3 Advanced Perceptual Contrast Algorithm
APCA models perceived contrast more accurately than the WCAG 2 relative luminance based algorithm, especially for dark mode and large text. APCA scores range different from WCAG 2.x ratios. Production target stays WCAG 2.2 4.5 to 1 until 3.0 reaches recommendation.
5.4 Outcome Based Criteria
WCAG 3.0 outcomes describe what users can do rather than technical conformance. Improves clarity but lengthens assessment.
5.5 Operational Posture
Target WCAG 2.2 AA. Document substantive accessibility work that maps to WCAG 3.0 outcomes (cognitive accessibility, plain language, consistent help). When 3.0 reaches recommendation, gap analysis is additive.
6. Semantic HTML and AI Parsing
Semantic HTML is the substrate accessibility layer and the substrate AI parsing layer. The same elements screen readers announce as landmarks, sections, headings, and controls are the elements AI engines parse for content structure.
6.1 Why Semantic HTML Lifts Both Surfaces
Surfer SEO October 2025 study of 41,200 URLs comparing pages with semantic landmark roles against pages with <div> based scaffolding, controlling for topical relevance and link profile, found 23 percent higher AI Overview citation rate on the semantic cohort. Mechanism: reading mode bots parse by structure. A page announcing main content via <main> is reliably extracted. A page where the engine must infer main content from layout heuristics is not.
6.2 The Landmark Pattern
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Title</title>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header>
<a href="/" aria-label="Home"><img src="/logo.svg" alt="Business Name"></a>
<nav aria-label="Main navigation">
<ul>
<li><a href="/services/">Services</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<article>
<header>
<h1>Page Title Matching Title Tag</h1>
<p class="lede">40 to 75 word lede answering the page's target query.</p>
</header>
<section aria-labelledby="section-1-heading">
<h2 id="section-1-heading">Section heading</h2>
<p>Answer first paragraph addressing the H2 question.</p>
</section>
</article>
<aside aria-labelledby="related-heading">
<h2 id="related-heading">Related resources</h2>
</aside>
</main>
<footer>
<p>Footer content.</p>
</footer>
</body>
</html>
Single <main> per page. Single <h1> per page. <nav> carries aria-label when multiple nav landmarks exist. <section> carries aria-labelledby referencing its own heading id.
6.3 Semantic Replacements
| Anti pattern | Semantic replacement |
|---|---|
<div class="header"> |
<header> |
<div class="nav"> |
<nav> |
<div class="main"> |
<main> |
<div class="footer"> |
<footer> |
<div class="sidebar"> |
<aside> |
<div class="article"> |
<article> |
<p style="font-size:32px;font-weight:bold;"> |
<h1>, <h2>, etc. |
<div onclick> |
<button> |
Series of <div> items |
<ul> or <ol> |
<table> for layout |
CSS grid or flexbox |
<div> for tabular data |
<table> with <thead>, <tbody>, <th scope> |
6.4 The First Byte Test
Every semantic markup commitment must survive the curl test:
curl -A "GPTBot" -s https://example.com/page | grep -E "<main|<nav|<h1|<header|<footer"
If the landmarks are not in first byte HTML, they are invisible to AI engines regardless of how they render in a browser. Client side hydrated markup fails reading mode parsing the same way it fails screen reader announcement on AT that does not wait for hydration.
7. Heading Hierarchy and Document Outline
Heading hierarchy is one of the highest leverage accessibility signals and one of the most direct AI citation signals. AI Overview snippets frequently quote H2 or H3 boundary content.
7.1 The Rules
- One H1 per page. Announces primary topic, matches visible page title, closely related to title tag.
- Sequential descent. H1 to H2, H2 to H3, H3 to H4. No skipping.
- Semantic match between heading and content. Text under heading addresses what heading promises.
- No heading tags for visual styling. Use CSS for size, heading levels for meaning.
- Headings describe section content, not position. H2 "Section 1" fails. H2 "How quarterly estimated taxes are calculated" passes.
7.2 The Outline Pattern
<main id="main-content">
<article>
<header>
<h1>How to Pay Quarterly Estimated Taxes in 2026</h1>
<p class="lede">...</p>
</header>
<section aria-labelledby="when-due">
<h2 id="when-due">When are quarterly taxes due in 2026</h2>
<p>Direct answer paragraph.</p>
</section>
<section aria-labelledby="how-to-pay">
<h2 id="how-to-pay">How to pay quarterly taxes</h2>
<p>Direct answer paragraph.</p>
<section aria-labelledby="irs-direct-pay">
<h3 id="irs-direct-pay">IRS Direct Pay</h3>
</section>
<section aria-labelledby="eftps">
<h3 id="eftps">EFTPS enrollment</h3>
</section>
</section>
</article>
</main>
7.3 Tooling
HeadingsMap Chrome extension visualizes the outline. Lighthouse flags missing H1 and out of order headings. Manual audit: navigate the page in NVDA or VoiceOver using H key and verify announced sequence.
7.4 H1 and Title Tag Relationship
<title> and <h1> are not required to match exactly. Title for SERP and browser tab (60 character limit, query match, brand). H1 for on page clarity (descriptive, complete sentence acceptable). Both communicate the same topic in different framings.
7.5 AI Overview Citation Boundary
AI Overview snippets often quote the first sentence under an H2 or H3 boundary. The answer first pattern under every H2 (40 to 75 word direct answer) gives the synthesis engine a citeable extraction surface. See framework-aioverviews.md Section 6.3.
8. Image Accessibility
Image accessibility intersects with image SEO. Comprehensive guidance in framework-imageseo.md. This section covers accessibility specific patterns.
8.1 Alt Text Writing Protocol
Alt text describes content function, not appearance. A photograph of an operator at a press in a safety article is "Operator wearing required PPE while loading material into industrial press" not "Photo of person at machine."
Categories:
alt_text_categories:
informational:
pattern: "Describe what the image conveys in context"
good: 'alt="Bar chart showing 40 percent revenue growth from Q1 to Q4 2026"'
bad: 'alt="Chart"'
decorative:
pattern: 'Empty alt attribute alt=""'
example: '<img alt="" src="/divider.svg" role="presentation">'
functional:
pattern: "Describe function not appearance"
good: '<img alt="Search" src="/search-icon.svg">'
bad: '<img alt="Magnifying glass icon" src="/search-icon.svg">'
complex:
pattern: "Alt text plus extended description nearby"
example: |
<img alt="14 tier framework diagram, full description below" src="/diagram.svg">
<p>The framework is organized in 14 tiers ascending...</p>
logos:
pattern: "Business name as alt"
example: '<img alt="ThatDeveloperGuy" src="/logo.svg">'
8.2 Anti Patterns
<!-- Missing alt, WCAG 1.1.1 failure -->
<img src="/hero.jpg">
<!-- Redundant phrasing, screen readers already announce as image -->
<img alt="Image of a dog playing fetch" src="/dog.jpg">
<!-- Filename as alt -->
<img alt="IMG_0432.jpg" src="/IMG_0432.jpg">
<!-- Empty alt on informational image -->
<img alt="" src="/important-chart.png">
<!-- Alt text on decorative image, adds noise -->
<img alt="Decorative swirl element" src="/swirl.svg">
8.3 SVG Accessibility
<!-- Inline SVG with title and desc -->
<svg viewBox="0 0 100 100" role="img" aria-labelledby="chart-title chart-desc">
<title id="chart-title">Q1 to Q4 Revenue Growth</title>
<desc id="chart-desc">Bar chart showing 40 percent growth across three product lines.</desc>
<rect x="10" y="50" width="20" height="40" fill="#4a90e2"></rect>
</svg>
<!-- Decorative inline SVG -->
<svg viewBox="0 0 100 100" role="presentation" aria-hidden="true">
<path d="..."></path>
</svg>
<!-- SVG icon inside a button -->
<button aria-label="Close dialog">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="..."></path></svg>
</button>
8.4 Background Images
CSS background images have no alt attributes. If a background image conveys meaning, the meaning must be available in HTML text. Never put meaningful content in CSS background images alone.
8.5 Figure and Figcaption
<figure>
<img src="/process-diagram.svg" alt="Five step content first publishing process">
<figcaption>Figure 1. Publishing process from intake through validation.</figcaption>
</figure>
Screen readers announce the figure landmark and read the caption. AI engines parse the figure as a content block with the caption as metadata.
8.6 AI Engine Image Parsing
Gemini 3 Pro and ChatGPT 5 extract content from alt text, surrounding text, captions, and figure markup. Descriptive alt text is consumed by the synthesis engine. A page with thoughtful alt text has more extractable surface area.
9. Form Accessibility
Forms are high stakes accessibility points and high stakes conversion points. The same patterns that make forms accessible lift completion rates. Comprehensive form optimization in framework-formoptimization.md.
9.1 Label For Pairing
<!-- Explicit label, preferred -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email" required>
<!-- Implicit label, acceptable -->
<label>
Email address
<input type="email" name="email" autocomplete="email" required>
</label>
<!-- Anti pattern: placeholder as label, fails WCAG 3.3.2 -->
<input type="email" placeholder="Email" name="email">
9.2 Autocomplete Attributes
WCAG 2.1 1.3.5 Identify Input Purpose requires autocomplete attributes on fields that collect user information. Also enables browser autofill which lifts completion.
<form>
<label for="given-name">First name</label>
<input type="text" id="given-name" name="given-name" autocomplete="given-name">
<label for="family-name">Last name</label>
<input type="text" id="family-name" name="family-name" autocomplete="family-name">
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email">
<label for="tel">Phone</label>
<input type="tel" id="tel" name="tel" autocomplete="tel">
<label for="street">Street address</label>
<input type="text" id="street" name="street" autocomplete="street-address">
<label for="city">City</label>
<input type="text" id="city" name="city" autocomplete="address-level2">
<label for="state">State</label>
<input type="text" id="state" name="state" autocomplete="address-level1">
<label for="postal">Postal code</label>
<input type="text" id="postal" name="postal" autocomplete="postal-code">
<label for="org">Organization</label>
<input type="text" id="org" name="org" autocomplete="organization">
</form>
9.3 Error Messaging
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
required
aria-required="true"
aria-invalid="false"
aria-describedby="email-help email-error">
<span id="email-help" class="help-text">We'll never share your email.</span>
<span id="email-error" role="alert" class="error-text" hidden>
Please enter a valid email address.
</span>
On validation failure:
const input = document.getElementById('email');
const error = document.getElementById('email-error');
input.setAttribute('aria-invalid', 'true');
error.hidden = false;
error.textContent = 'Please enter a valid email address.';
Screen reader announces error when role="alert" becomes visible.
9.4 Fieldset and Legend
<fieldset>
<legend>Shipping address</legend>
<label for="ship-street">Street</label>
<input type="text" id="ship-street" name="ship-street" autocomplete="shipping street-address">
<label for="ship-city">City</label>
<input type="text" id="ship-city" name="ship-city" autocomplete="shipping address-level2">
</fieldset>
<fieldset>
<legend>Preferred contact method</legend>
<input type="radio" id="contact-email" name="contact" value="email">
<label for="contact-email">Email</label>
<input type="radio" id="contact-phone" name="contact" value="phone">
<label for="contact-phone">Phone</label>
</fieldset>
9.5 Required Indicators
<label for="name">
Full name <span class="required-indicator" aria-hidden="true">*</span>
<span class="visually-hidden">(required)</span>
</label>
<input type="text" id="name" name="name" autocomplete="name" required aria-required="true">
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
9.6 Honeypot and CAPTCHA Tradeoff
CAPTCHA is hostile to screen reader users and keyboard only users. Honeypot fields (hidden input expected to remain empty, indicating bot if filled) are accessible because real users never see them. For lead and contact forms, honeypot plus server side validation typically catches enough spam without CAPTCHA. Where CAPTCHA is required, hCaptcha and reCAPTCHA v3 (scoring based, no challenge) are more accessible than image grid challenges.
10. Color and Contrast
WCAG 2.2 contrast requirements:
- 1.4.3 Contrast (Minimum) AA. Normal text 4.5 to 1. Large text (18 point or 14 point bold) 3 to 1.
- 1.4.6 Contrast (Enhanced) AAA. Normal text 7 to 1. Large text 4.5 to 1.
- 1.4.11 Non text Contrast AA. UI components and graphical objects 3 to 1.
10.1 The 4.5 to 1 Floor
Use WebAIM Contrast Checker or Chrome DevTools color picker. Common failures: light gray body text on white (often 3.5 to 1), light brand color text on white (often below 3 to 1), white text on light backgrounds.
10.2 Large Text Exception
Text at 18 point (24 pixel) and above, or 14 point (18.66 pixel) bold and above, needs only 3 to 1.
10.3 UI Component Contrast
WCAG 2.1 1.4.11 requires UI components (buttons, form input borders, focus indicators, state icons) and graphical objects to hit 3 to 1 against adjacent colors.
10.4 Brand Color Exceptions
Brand color is permissible in non text decorative contexts and prohibited as primary body or CTA text unless contrast passes. CTA solution: pair brand color with dark text overlay or use brand color only as accent.
10.5 Color Never Sole Information Carrier
WCAG 1.4.1 Use of Color. Information conveyed by color must also be conveyed by another means.
<!-- Failure: required marked by red color only -->
<label>Name</label>
<input type="text" name="name" style="border-color: red;">
<!-- Pass: red plus asterisk plus text -->
<label>Name <span aria-hidden="true">*</span><span class="visually-hidden">(required)</span></label>
<input type="text" name="name" required aria-required="true">
10.6 Dark Mode Contrast
:root {
--color-text: #1a1a1a;
--color-background: #ffffff;
--color-link: #0066cc;
}
@media (prefers-color-scheme: dark) {
:root {
--color-text: #f0f0f0;
--color-background: #1a1a1a;
--color-link: #4a9eff;
}
}
body { color: var(--color-text); background: var(--color-background); }
a { color: var(--color-link); }
10.7 WCAG 3.0 APCA Preview
APCA scores range from -108 to +106 rather than ratio. Approximate targets: 75 for body text, 60 for large text, 45 for UI components. Tooling at apcacontrast.com. Production target stays WCAG 2.2 4.5 to 1 until WCAG 3.0 reaches recommendation.
10.8 Focus Indicator Contrast
The focus indicator must hit 3 to 1 contrast against the unfocused state and background.
:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
@media (prefers-color-scheme: dark) {
:focus-visible { outline-color: #4a9eff; }
}
:focus-visible shows the indicator only on keyboard focus, not on mouse click.
11. Motion and Animation
Motion accessibility addresses three populations: users with vestibular disorders (parallax nausea), users with photosensitive epilepsy (flashing triggers), and users with attention differences (autoplay disruption).
11.1 prefers-reduced-motion
OS setting "Reduce motion" exposes preference via CSS media query.
.hero-animation { animation: slide-in 0.8s ease-out; }
.parallax { transform: translateY(var(--scroll-y)); transition: transform 0.1s linear; }
@media (prefers-reduced-motion: reduce) {
.hero-animation,
.parallax {
animation: none;
transition: none;
transform: none;
}
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
11.2 Autoplay Video
WCAG 2.2 1.4.2 prohibits autoplay with audio for more than 3 seconds without user control. Operational rule: never autoplay with audio. Muted autoplay is permitted but should pause on prefers-reduced-motion: reduce.
<video autoplay muted loop playsinline aria-label="Decorative background showing workspace">
<source src="/hero.mp4" type="video/mp4">
</video>
<button id="pause-video" aria-pressed="false">Pause background video</button>
const video = document.querySelector('video[autoplay]');
const button = document.getElementById('pause-video');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
video.pause();
button.setAttribute('aria-pressed', 'true');
}
button.addEventListener('click', () => {
if (video.paused) {
video.play();
button.setAttribute('aria-pressed', 'false');
} else {
video.pause();
button.setAttribute('aria-pressed', 'true');
}
});
11.3 Parallax and Large Scale Motion
Enable parallax only when user has not requested reduced motion. Provide settle states that complete quickly when reduced motion is requested.
11.4 Flashing Content
WCAG 2.2 2.3.1 prohibits content flashing more than three times per second. Animated icons, status indicators, and decorative effects must stay below 3 Hz.
11.5 Carousel Auto Advance
Auto advancing carousels must have pause, stop, and hide controls. Prefer non auto advancing carousels or grid layouts. Carousels rank low in engagement research and high in accessibility complexity.
12. Keyboard and Focus Management
Every interactive element must be reachable, operable, and identifiable via keyboard alone.
12.1 Visible Focus Indicator
:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
Never use outline: none without a replacement. WCAG 2.2 2.4.7 Focus Visible is critical AA.
12.2 Tab Order
Tab order follows visual reading order. Avoid positive tabindex values (1, 2, 3) which override natural order. Use tabindex="0" to make non focusable element focusable, tabindex="-1" to make element programmatically focusable but not in tab sequence.
12.3 Skip Links
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header><nav>...</nav></header>
<main id="main-content" tabindex="-1">...</main>
</body>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px 12px;
z-index: 100;
text-decoration: none;
}
.skip-link:focus { top: 0; }
</style>
12.4 Focus Trap in Modals
<button id="open-modal" aria-haspopup="dialog">Open dialog</button>
<div id="dialog" role="dialog" aria-modal="true" aria-labelledby="dialog-title" hidden>
<h2 id="dialog-title">Dialog title</h2>
<p>Dialog content.</p>
<button id="close-modal">Close</button>
</div>
const trigger = document.getElementById('open-modal');
const dialog = document.getElementById('dialog');
const closeBtn = document.getElementById('close-modal');
let previouslyFocused;
function openDialog() {
previouslyFocused = document.activeElement;
dialog.hidden = false;
closeBtn.focus();
document.addEventListener('keydown', trapFocus);
}
function closeDialog() {
dialog.hidden = true;
document.removeEventListener('keydown', trapFocus);
previouslyFocused.focus();
}
function trapFocus(e) {
if (e.key === 'Escape') { closeDialog(); return; }
if (e.key !== 'Tab') return;
const focusable = dialog.querySelectorAll(
'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
trigger.addEventListener('click', openDialog);
closeBtn.addEventListener('click', closeDialog);
12.5 Custom Component Patterns
Custom components (comboboxes, listboxes, menus, tabs, accordions) follow ARIA Authoring Practices Guide patterns. If full APG keyboard pattern cannot be implemented, use the native HTML control. A <select> is a fully accessible combobox by default.
12.6 Focus After Route Change in SPAs
function handleRouteChange() {
const heading = document.querySelector('main h1');
if (heading) {
heading.setAttribute('tabindex', '-1');
heading.focus();
}
}
Move focus to new page's main heading. Announce via role="status" or aria-live="polite" region.
13. Testing Methodology
Three layers: automated, manual technical, user testing with assistive technology. Each catches issues the others miss.
13.1 Automated Testing
Automated tools catch approximately 30 to 50 percent of WCAG issues. Necessary but not sufficient.
| Tool | Coverage | Integration |
|---|---|---|
| axe DevTools | Comprehensive WCAG 2.2 ruleset, ~125 rules in free tier | Browser extension, CI via axe-core CLI |
| WAVE | Visual indicator overlay, contrast checking | Browser extension and web tool |
| Lighthouse | Subset of axe rules | Chrome DevTools, Lighthouse CI |
| Pa11y | axe-core or HTML CodeSniffer | CLI and CI |
| Accessibility Insights | Microsoft fast pass | Browser extension |
Automated tools verify: missing alt, unlabeled inputs, contrast, ARIA validity, heading violations, duplicate IDs, empty buttons and links, missing lang attribute, missing title, document structure.
Automated tools cannot verify: whether alt text is meaningful, whether heading text is descriptive, whether keyboard navigation makes logical sense, whether screen reader announcement is useful, whether color is sole information carrier.
13.2 Manual Keyboard Test
keyboard_test_protocol:
step_1_tab_forward:
action: "Press Tab from URL bar through end of page"
verify:
- "Every interactive element focused in visual order"
- "Focus indicator visible on every focused element"
- "Skip link appears as first focusable item"
- "No unintended focus traps"
step_2_shift_tab_reverse:
action: "Shift Tab back through page"
verify: "Same elements reachable in reverse"
step_3_activate:
action: "Tab to each interactive element, Enter or Space"
verify:
- "Links navigate on Enter"
- "Buttons activate on Enter and Space"
- "Form inputs accept input"
step_4_modals:
action: "Open each modal"
verify:
- "Focus moves into modal on open"
- "Tab cycles within modal"
- "Escape closes modal"
- "Focus returns to trigger on close"
13.3 Manual Screen Reader Test
Test matrix: VoiceOver on macOS Safari, NVDA on Windows Firefox, JAWS on Windows Chrome (enterprise procurement). For most engagements, VoiceOver plus NVDA covers majority.
screen_reader_test_protocol:
step_1_overview:
voiceover: "VO + U to open rotor, navigate landmarks"
nvda: "Insert + F7 for elements list"
verify: "Main, navigation, header, footer announced; single H1; logical outline"
step_2_content:
action: "Navigate via heading shortcut (H in NVDA, VO+Cmd+H in VoiceOver)"
verify: "Logical sequence; content addresses headings; no 'click here' anchor text"
step_3_forms:
action: "Tab into each form field"
verify: "Label announced; required state announced; help text via aria-describedby; errors announced on failure"
step_4_dynamic:
action: "Trigger form errors, alerts, modal open, route change"
verify: "Updates announced via aria-live or role=alert; focus management correct"
13.4 Zoom and Reflow Test
WCAG 1.4.10 Reflow requires content at 320 CSS pixel width without two dimensional scrolling. Test by setting browser to 400 percent zoom on 1280 pixel window.
13.5 Forced Colors
Windows High Contrast and macOS Increased Contrast change colors at OS level. CSS forced-colors media query indicates user has enabled it. Use standard CSS keywords: Canvas, CanvasText, LinkText, ButtonText, ButtonFace.
@media (forced-colors: active) {
.focus-indicator { outline: 2px solid CanvasText; }
.button-primary {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
}
13.6 AT User Testing
When legal exposure matters or enterprise client requires evidence, user testing with people who use assistive technology daily catches what developer testing misses. Typical engagement: 5 to 8 sessions covering screen reader users, keyboard only users, low vision users, cognitive accessibility users.
14. Bubbles Hosted Accessibility Toolchain
Accessibility toolchain runs self hosted on Bubbles Debian server at 169.155.162.118. No third party proxy or external observability. Audit data stays on infrastructure Joseph controls.
14.1 Server Layout
/home/user/a11y/
node_modules/ # axe-core CLI, Pa11y, Lighthouse CI
package.json
scripts/
audit-site.sh
audit-page.sh
weekly-sweep.sh
config/
pa11y.json
lighthouse.json
14.2 axe-core CLI Installation
mkdir -p /home/user/a11y && cd /home/user/a11y
cat > package.json <<'EOF'
{
"name": "a11y-tools",
"version": "1.0.0",
"private": true,
"dependencies": {
"@axe-core/cli": "^4.10.0",
"pa11y": "^8.0.0",
"pa11y-ci": "^3.1.0",
"lighthouse": "^12.0.0",
"@lhci/cli": "^0.14.0"
}
}
EOF
npm install
14.3 Pa11y CI Configuration
{
"defaults": {
"standard": "WCAG2AA",
"timeout": 60000,
"wait": 1000,
"chromeLaunchConfig": {
"args": ["--no-sandbox", "--disable-dev-shm-usage"]
}
},
"urls": [
"https://example.com/",
"https://example.com/services/",
"https://example.com/about/",
"https://example.com/contact/"
]
}
/home/user/a11y/node_modules/.bin/pa11y-ci --config /home/user/a11y/config/pa11y.json
14.4 Lighthouse CI Configuration
{
"ci": {
"collect": {
"url": ["https://example.com/", "https://example.com/services/"],
"numberOfRuns": 3
},
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"categories:accessibility": ["error", { "minScore": 0.95 }]
}
},
"upload": {
"target": "filesystem",
"outputDir": "/home/user/a11y/lhci-reports"
}
}
}
14.5 Weekly Cron Sweep
crontab -e
# Add:
0 3 * * 1 /home/user/a11y/scripts/weekly-sweep.sh
#!/usr/bin/env bash
# /home/user/a11y/scripts/weekly-sweep.sh
set -e
REPORT_DIR="/home/user/a11y/reports/$(date -u +%Y-%m-%d)"
mkdir -p "${REPORT_DIR}"
SITES=(
"https://thatdeveloperguy.com"
"https://thataiguy.org"
"https://eurekabathworks.com"
"https://heritagehardwoodfloorsllc.com"
)
for site in "${SITES[@]}"; do
site_slug=$(echo "${site}" | sed 's|https://||;s|[^a-zA-Z0-9]|_|g')
/home/user/a11y/node_modules/.bin/axe "${site}" \
--tags wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa \
--save "${REPORT_DIR}/axe-${site_slug}.json" || true
/home/user/a11y/node_modules/.bin/pa11y "${site}" \
--reporter json > "${REPORT_DIR}/pa11y-${site_slug}.json" || true
done
echo "Audit complete. Reports in ${REPORT_DIR}"
14.6 Automated CI on Every Deploy
#!/usr/bin/env bash
# /var/www/sites/[domain]/scripts/a11y-ci.sh
set -e
SITE_URL="${1:-https://example.com}"
REPORT_DIR="/var/www/sites/[domain]/a11y-reports"
TIMESTAMP=$(date -u +"%Y%m%dT%H%M%SZ")
mkdir -p "${REPORT_DIR}"
PAGES=(
"${SITE_URL}/"
"${SITE_URL}/services/"
"${SITE_URL}/about/"
"${SITE_URL}/contact/"
)
FAIL=0
for url in "${PAGES[@]}"; do
echo "Auditing: ${url}"
/home/user/a11y/node_modules/.bin/axe "${url}" \
--tags wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa \
--save "${REPORT_DIR}/axe-${TIMESTAMP}-$(echo "${url}" | sed 's|[^a-zA-Z0-9]|_|g').json" \
--exit || FAIL=1
done
if [ "${FAIL}" -eq 1 ]; then
echo "Accessibility violations found. See reports in ${REPORT_DIR}"
exit 1
fi
echo "All audited pages pass."
Run on every deploy. Fail deploy on critical or serious violations.
14.7 nginx Configuration
server {
listen 443 ssl http2;
server_name example.com;
location ~* \.(svg|woff2|woff)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
add_header Content-Language "en";
try_files $uri $uri/ =404;
}
location = /accessibility/ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
}
Framework does not recommend any third party CDN or proxy. nginx on Bubbles serves all client sites directly. Origin is 169.155.162.118. Caching, headers, and TLS terminate on Bubbles. Audit data, server logs, and user behavior signals stay on infrastructure Joseph controls.
14.8 Report Storage
Reports land in /home/user/a11y/reports/[YYYY-MM-DD]/. Directory tree is the audit trail. Client deliverables export to PDF via headless Chrome and share via the Bubbles audit tool in project_audit_tool.md.
15. Audit Mode and Common Failures
15.1 First Pass Audit (Per Page)
For existing site engagement, this five item subset identifies whether the page is fundamentally accessible. Any failure means not WCAG 2.2 AA conformant regardless of other work.
| # | Criterion | Pass/Fail |
|---|---|---|
| A1 | curl test returns semantic landmarks (main, header, footer, nav) in first byte | |
| A2 | Single H1 per page, sequential heading descent | |
| A3 | All images have appropriate alt text | |
| A4 | All form inputs have associated labels and autocomplete attributes | |
| A5 | Color contrast 4.5 to 1 body text, 3 to 1 large text and UI components |
15.2 Per Page Audit (Full)
| # | Criterion | Pass/Fail |
|---|---|---|
| P1 | Lighthouse accessibility score 95+ mobile | |
| P2 | axe DevTools zero critical zero serious violations | |
| P3 | WAVE zero errors zero contrast errors | |
| P4 | Semantic HTML throughout, landmarks present | |
| P5 | Heading hierarchy sequential, one H1 per page | |
| P6 | All images have appropriate alt text | |
| P7 | All interactive elements keyboard accessible with visible focus | |
| P8 | Skip link present and operable | |
| P9 | Color contrast meets WCAG 2.2 AA | |
| P10 | Color not sole information carrier | |
| P11 | Forms have proper labels, autocomplete, error association, fieldset | |
| P12 | Video has captions, audio has transcript | |
| P13 | ARIA used appropriately, semantic HTML preferred where available | |
| P14 | prefers-reduced-motion respected | |
| P15 | No autoplay with audio | |
| P16 | Modal dialogs trap focus and restore on close | |
| P17 | Custom components follow ARIA Authoring Practices Guide patterns | |
| P18 | Manually tested with NVDA or VoiceOver | |
| P19 | Page reflows at 400 percent zoom without horizontal scroll | |
| P20 | Forced colors mode usable |
Score 20. World class accessibility per page: 18 or higher with zero A1 to A5 fails.
15.3 Site Wide Audit
| # | Criterion | Pass/Fail |
|---|---|---|
| S1 | WCAG 2.2 AA conformance documented and tested | |
| S2 | Accessibility statement published at /accessibility/ | |
| S3 | Independent audit within last 12 months | |
| S4 | Automated CI on every deploy | |
| S5 | Manual keyboard and screen reader testing cadence operational | |
| S6 | AT user testing completed where legal exposure warrants | |
| S7 | Remediation evidence documented | |
| S8 | Jurisdiction specific exposure assessed (ADA, EAA, AODA, 508, Unruh) | |
| S9 | YMYL pages meet WCAG 2.2 AAA where reasonable | |
| S10 | Bubbles hosted toolchain operational, weekly audit reports archived |
Score 10. World class accessible site: 9 or higher with zero fails on S1, S2, S4.
15.4 Common Failure Patterns
- Treating accessibility as edge case. Roughly 25 percent of US adults live with disability.
- Automated tools only. Catches 30 to 50 percent. Manual testing covers the rest.
- Placeholder as label. Disappears when typing. WCAG 3.3.2 failure.
- Color only differentiation. Pair color with text or icon.
- No keyboard testing. Many users cannot use a mouse.
outline: nonewithout replacement. WCAG 2.4.7 failure.- ARIA over semantic HTML.
<div role="button" tabindex="0">instead of<button>. Use semantic HTML first. - Auto captions without correction. Always review and correct.
- Inaccessible third party widgets. Replace or document gap in accessibility statement.
- Compliance theater. Accessibility statement claiming WCAG 2.2 AA on non conforming site raises rather than lowers exposure.
- Missing autocomplete attributes. WCAG 1.3.5 failure. Reduces form completion via disabled autofill.
- Heading skip. H1 to H3 directly fails screen reader navigation. Common in CMS templates.
- Decorative images with alt text. Adds noise. Empty
alt=""is correct. - Informational images with empty alt. Removes information from screen reader users. WCAG 1.1.1.
- Modal that does not trap focus. Tab leaves modal, reaches background content.
15.5 Remediation Priority Order
- Critical (axe critical, screen reader broken, keyboard inaccessible primary flows)
- Serious (contrast failures on primary text, missing form labels, missing alt on informational images)
- Moderate (heading hierarchy, ARIA misuse not blocking access)
- Minor (decorative image with non empty alt, redundant ARIA)
- AAA aspirational (enhanced contrast, sign language, no time limits)
16. Maintenance Schedule and Report Templates
16.1 Maintenance Cadence
Daily. CI on every deploy runs axe-core and Pa11y. Failed deploys block merge. Weekly. Bubbles cron sweep across priority pages. Manual keyboard test on new features. Monthly. Manual screen reader test on changed pages. Review audit trends. Update accessibility statement if conformance changed. Quarterly. Full Section 15 audit. Refresh accessibility statement. Brief stakeholder on findings. Annually. Independent third party audit. Refresh against current WCAG version. Strategic review of legal exposure.
16.2 Audit Report Template
# Accessibility Framework Audit Report
**Site**: {{BUSINESS_NAME}}
**Audit Date**: {{TODAY}}
**WCAG Version Targeted**: 2.2 AA
**Jurisdiction**: {{JURISDICTION}}
## Executive Summary
One paragraph assessment of conformance posture, top three risks, recommended remediation order.
**Site wide score**: X/10
**Average priority page score**: X/20
**Critical violations**: X
**Serious violations**: X
**Legal exposure assessment**: NONE | LOW | MODERATE | HIGH
## Findings by Section
Section 6 Semantic HTML, Section 7 Heading Hierarchy, Section 8 Image Accessibility, Section 9 Form Accessibility, Section 10 Color and Contrast, Section 11 Motion and Animation, Section 12 Keyboard and Focus.
## Critical Failures
List with location, WCAG criterion, severity, remediation path.
## First Pass Subset Findings
Per priority page table of A1 to A5 results.
## Recommended Remediation Order
1. Critical
2. Serious
3. Moderate
4. Minor
## Sign-Off
16.3 Implementation Report Template
# Accessibility Framework Implementation Report
**Site**: {{BUSINESS_NAME}}
**Implementation Date**: {{TODAY}}
**WCAG Target**: 2.2 AA
## Summary
- Pages remediated: X of Y
- Critical violations remediated: X of Y
- Serious violations remediated: X of Y
- Accessibility statement published: Yes/No
- Automated CI operational: Yes/No
- Weekly manual cadence operational: Yes/No
## Substrate Work
Semantic markup pass count, heading hierarchy corrections, form label additions, autocomplete additions, alt text rewrites.
## Color and Motion Work
Contrast remediations, prefers-reduced-motion handling, autoplay video remediation.
## Testing Infrastructure
axe-core CLI installed on Bubbles, Pa11y CI configured, Lighthouse CI configured, weekly cron sweep operational.
## Sign-Off
End of Framework Document
Document version: 2.0 Created: 2026-05-14 Maintained by: ThatDeveloperGuy
Accessibility in 2026 is structural quality. The same semantic markup screen readers consume is what AI engines parse for citation. The same heading hierarchy supporting screen reader navigation supports AI Overview answer extraction. The same form labels making a field operable by assistive technology raise the form completion rate. The disciplines reinforce each other so completely that treating accessibility as separate from SEO and AEO is operational waste.
Apply in parallel with framework-uxseo.md, framework-pageexperience.md, framework-mobileseo.md, framework-imageseo.md, framework-videoseo.md, framework-formoptimization.md, framework-schema.md, framework-technicalseo.md, framework-eeat.md, and framework-aicitations.md. For React and Tailwind specific guidance see framework-react.md and framework-tailwind.md.
Companions
- framework-uxseo.md, broader UX layer consuming accessibility output
- framework-pageexperience.md, Core Web Vitals and performance interaction with motion and image sizing
- framework-mobileseo.md, mobile specific tap targets and viewport
- framework-imageseo.md, comprehensive image SEO including alt text guidance
- framework-videoseo.md, video and audio accessibility, captions and transcripts
- framework-formoptimization.md, form conversion optimization with accessibility as foundation
- framework-schema.md, graph patterns complementing semantic markup
- framework-technicalseo.md, bot facing foundation paired with this human facing layer
- framework-eeat.md, credibility patterns paired with author profile accessibility
- framework-aicitations.md, AI citation mechanics consuming semantic markup
- framework-aioverviews.md, Google AI Overview citation architecture
- framework-react.md, React specific accessibility patterns
- framework-tailwind.md, Tailwind specific focus, contrast, dark mode
Want this framework implemented on your site?
ThatDevPro ships these frameworks as productized services. SDVOSB-certified veteran owned. Cassville, Missouri.
See Engine Optimization service ›