--- name: web-accessibility description: 'Use this skill whenever the user is building or reviewing any web UI. Accessibility is a baseline requirement, not a niche concern. Covers WCAG 2.2 AA, semantic HTML, ARIA patterns, keyboard navigation, focus management, color contrast, touch targets, forms, and testing with axe / screen readers. Trigger when generating any interactive component or page (forms, modals, menus, tables, navigation, layouts), before any "launch" or "go live", during pre-launch checklists, and whenever producing new HTML/JSX markup, even if the user does not explicitly mention a11y or WCAG. Skip for backend-only changes, CLI tools, or non-UI code.' --- # Web Accessibility Guide > Applies to: Any website or web app | Updated: March 2026 A practical reference for building accessible websites - covering WCAG 2.2 criteria, semantic HTML, ARIA, forms, touch, and testing. --- ## Section 0: Before You Start Answer these questions before generating accessibility code. Each answer changes which sections apply. **Q: Where are your users?** Default: worldwide - EU/UK requirements (EAA, PSBAR) will be flagged as applicable throughout this guide. Options: US only | Europe/EU | UK | Worldwide **Q: What kind of product is this?** Default: company/marketing website Options: Company/marketing website | Online shop/e-commerce | Web app/SaaS | Media/video site | Document tool **Q: Will people use this on mobile phones or tablets as well as desktop?** Default: yes - touch target sizing and pointer accessibility sections apply. **Q: Are you building with a component library?** Default: none Options: Radix UI | shadcn/ui | MUI | Chakra UI | Other | None - if a library is detected in `package.json`, note which accessibility features it handles natively so you don't duplicate them. **Q: Do you need to comply with a specific standard for legal or contract reasons?** Default: WCAG 2.2 AA - current best-practice baseline. Options: WCAG 2.1 AA | WCAG 2.2 AA | Section 508 | EN 301 549 | Not sure > **AI assistant:** Use these answers to prioritize. For EU products: apply EAA compliance notes. For mobile: apply touch target and pointer sections. If a known accessible component library is detected, note which patterns it handles so you don't duplicate effort. --- ## Contents 1. [Legal & Compliance](#legal--compliance) 2. [WCAG Standards Overview](#wcag-standards-overview) 3. [WCAG 2.2 - What's New](#wcag-22--whats-new) 4. [Semantic HTML](#semantic-html) 5. [Keyboard Navigation](#keyboard-navigation) 6. [ARIA](#aria) 7. [Images & Media](#images--media) 8. [Forms](#forms) 9. [Touch & Pointer](#touch--pointer) 10. [Color & Contrast](#color--contrast) 11. [Focus Management](#focus-management) 12. [Motion & Animation](#motion--animation) 13. [Testing](#testing) 14. [Anti-Patterns to Flag](#anti-patterns-to-flag) 15. [Checklist by Product Type](#checklist-by-product-type) --- ## Legal & Compliance Accessibility is a legal requirement across most major markets. Non-compliance carries real risk - fines, market access restrictions, and litigation. | Jurisdiction | Law / Standard | Scope | Notes | |---|---|---|---| | **EU** | European Accessibility Act (EAA) | Private-sector e-commerce, banking, transport, streaming. Enforceable since June 28, 2025. | Standard: EN 301 549 v3.2.1, which references WCAG 2.1 AA; WCAG 2.2 strongly recommended. Non-compliance = market access restrictions. | | **US** | ADA Title III | Private-sector websites and apps | ~4,000 - 4,500 lawsuits/year; e-commerce and hospitality are primary targets. Overlay-only solutions have been named in lawsuits as insufficient. | | **US Federal** | Section 508 | Federal agencies and contractors | References WCAG 2.0 AA. Agencies procuring software must meet this baseline. | | **UK** | Web Accessibility Regulations 2018 + Equality Act 2010 | Public sector: WCAG 2.2 AA required. Private sector: Equality Act applies. | PSBAR (Public Sector Bodies Accessibility Regulations) requires a published accessibility statement. | **Overlay warning:** Single-script accessibility overlays (AccessiBe, UserWay, etc.) do not achieve WCAG conformance and have been directly challenged in ADA litigation. Flag this clearly if a user asks about "automatic" or "one-line" accessibility fixes. --- ## WCAG Standards Overview WCAG (Web Content Accessibility Guidelines) uses three conformance levels: - **Level A** - minimum baseline; blocking failures (e.g. images with no `alt`) - **Level AA** - standard legal requirement across most jurisdictions (e.g. 4.5:1 contrast) - **Level AAA** - enhanced; not required site-wide but worth targeting for specific features **Target WCAG 2.2 Level AA** as your default baseline. **On WCAG 3.0:** It is a research-stage Working Draft with no published adoption timeline. It uses a different scoring model and will not replace WCAG 2.x for the foreseeable future. Do not use it as a current compliance target. --- ## WCAG 2.2 - What's New WCAG 2.2 was published October 2023. These criteria are not in most existing guides or tools. They represent the highest-value additions to implement. ### 2.4.11 Focus Appearance (AA) The focus indicator must meet two conditions simultaneously: 1. **Area:** The focus indicator area must be at least as large as a 2px perimeter outline around the unfocused component. 2. **Contrast:** The color change between focused and unfocused states must have a contrast ratio of at least 3:1. A 1px dotted border fails both. This goes beyond "don't remove `outline`." ```css /* Fails 2.4.11 - thin, low-contrast */ :focus { outline: 1px dotted #999; } /* Passes 2.4.11 - thick, high-contrast */ :focus-visible { outline: 3px solid #005fcc; /* Check #005fcc vs background meets 3:1 */ outline-offset: 2px; } ``` ### 2.5.7 Dragging Movements (AA) Any interaction that uses drag-and-drop, slider, or map pan must have a single-pointer (click/tap) alternative. Drag is not a safe assumption - users with motor disabilities cannot rely on it. ```html
  • Item label
  • ``` ### 2.5.8 Target Size Minimum (AA) Interactive targets must be at least **24×24 CSS pixels**, or have spacing such that the total clickable zone (target + surrounding space) reaches 24×24. Best practice: 44×44px (Apple HIG), 48×48px (Material Design). ```css /* Minimum compliant - 24×24px */ .icon-btn { min-width: 24px; min-height: 24px; } /* Best practice - 44×44px tap target */ .icon-btn { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; } ``` ### 3.3.8 Accessible Authentication (AA) Login and signup flows cannot rely solely on a cognitive test (e.g. image CAPTCHA with no alternative). Acceptable alternatives: passkeys, magic link via email, CAPTCHA with an audio alternative, or copy-paste support in the password field. Do not block clipboard paste in password fields - this breaks password managers and fails this criterion. ### 3.3.7 Redundant Entry (AA) Multi-step forms must not ask for the same information twice unless it is essential to re-enter. Auto-populate or display previously entered data. ```html ``` ### 3.2.6 Consistent Help (AA) If a help mechanism (chat widget, phone number, help link, contact form) appears on more than one page, it must appear in the same relative location on every page. ### 4.1.1 Parsing - Removed in WCAG 2.2 WCAG 2.2 removed criterion 4.1.1 Parsing. Modern browsers reliably handle malformed HTML; it no longer represents an accessibility barrier. Automated tools that still report 4.1.1 violations are reporting against a deprecated criterion. Remove it from test baselines if you're targeting WCAG 2.2. --- ## Semantic HTML Semantic HTML is the highest-value accessibility improvement - it is free, requires no ARIA, and works with all assistive technologies. ### Page structure ```html Page Title - Site Name

    Page Heading

    Section Title

    ``` For inline content in a different language, add the `lang` attribute on the element - screen readers use this to switch pronunciation: ```html

    The French say bonjour as a greeting.

    ``` ### Use the right element Native HTML elements carry built-in keyboard support, roles, and states. Prefer them over custom implementations. Use ` ``` ### Keyboard interactions for custom components | Component | Expected keys | |---|---| | Button | Enter, Space to activate | | Link | Enter to follow | | Checkbox | Space to toggle | | Radio group | Arrow keys to move between options | | Dropdown/Select | Arrow keys to navigate, Enter to select, Escape to close | | Dialog/Modal | Escape to close; focus trapped inside while open | | Tabs | Arrow keys to switch tabs | | Accordion | Enter/Space to expand/collapse | --- ## ARIA ARIA (Accessible Rich Internet Applications) adds semantic meaning when native HTML elements are insufficient. Use it as a last resort. > Do not use ARIA if a native HTML element already has the semantics you need. ### aria-label and aria-labelledby Use `aria-label` when there is no visible text label: ```html ``` `aria-label` text must match or begin with the visible button text (WCAG 2.5.3 Label in Name) - otherwise voice control users who say "click Close" cannot activate it. Use `aria-labelledby` to reference existing visible text, which avoids duplication: ```html

    Billing

    ``` ### aria-describedby Points to supplementary description - announced after the element's name and role: ```html

    Must be at least 8 characters with one number.

    ``` ### aria-expanded Toggle between `"true"` and `"false"` as the element opens and closes: ```html ``` ### aria-hidden Hides decorative elements from screen readers. Never apply to focusable elements - a hidden-but-focusable element creates an invisible, unannounced interactive target: ```html ``` ### aria-live Announces dynamic content updates. Use `polite` for the vast majority of cases - it waits until the screen reader finishes the current sentence before reading the update. Reserve `assertive` for genuine, time-critical errors that require interrupting the user immediately: ```html
    ``` `aria-atomic="true"` re-reads the entire region when any part changes, rather than just the changed portion. Use it when partial announcements would be confusing (e.g. "3 results" vs "Showing 3 results for 'shoes'"). ### Common roles ```html
    Error: email is required.
    3 results found.

    Confirm Delete

    ``` --- ## Images & Media ### Alt text Every `` must have an `alt` attribute: ```html Bar chart showing 40% increase in Q4 sales Go to homepage ``` ### SVG ```html ... ``` ### Video & audio - Add captions to all video with speech or meaningful sound (``) - Provide transcripts for audio-only content - Do not autoplay audio or video with sound ```html ``` --- ## Forms Forms are one of the most common accessibility failure points. ### Labels Every input needs a visible, associated label. Never use `placeholder` as a label - it disappears on input and typically fails contrast requirements: ```html ``` ### autocomplete tokens (WCAG 1.3.5 - Identify Input Purpose) Add `autocomplete` tokens to personal data fields. This enables password managers, browser autofill, and assistive technology for users with cognitive disabilities: ```html ``` ### Required fields For native `` elements, the `required` attribute is sufficient - it is announced by screen readers and triggers native validation. Add `aria-required="true"` only for custom components that don't use native form elements: ```html

    Fields marked are required.

    ...
    ``` ### Error messages Link errors to their input using `aria-describedby`. Set `aria-invalid="true"` when an error exists; remove it when the value becomes valid: ```html ``` ### Fieldsets for grouped inputs Group related inputs (radio buttons, checkboxes) with `
    ` and `` - the legend is announced before each option, providing essential context: ```html
    Preferred contact method
    ``` ### Multi-step forms (WCAG 3.3.7) Don't ask for the same data twice. Pre-populate fields using previously entered values. Show a summary of entered data before the final submit step. --- ## Touch & Pointer ### Minimum touch target size (WCAG 2.5.8) Interactive targets must be at least 24×24 CSS pixels. Best practice is 44×44px: ```css /* Best practice: 44×44px tap target, even for small visual elements */ .btn-icon { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; } /* Touchscreen-specific adjustments */ @media (pointer: coarse) { .btn-icon { min-width: 48px; min-height: 48px; } } ``` `pointer: coarse` targets touchscreens and devices where the pointing device has limited accuracy. Use it for any touch-specific spacing or sizing overrides. ### 1.4.13 Content on Hover or Focus Tooltips, sub-menus, and dropdowns that appear on hover or focus must meet three conditions: 1. **Dismissable** - the user can dismiss the tooltip without moving pointer or focus (Escape key closes it) 2. **Hoverable** - the mouse can move from the trigger into the tooltip without it disappearing 3. **Persistent** - the tooltip stays visible until the user dismisses it, moves focus, or removes the pointer ```css /* Wrong: tooltip disappears when pointer leaves trigger */ .trigger:hover .tooltip { display: block; } /* Correct: tooltip stays open when pointer moves into it */ .trigger:hover .tooltip, .tooltip:hover { display: block; } ``` Also expose tooltip content via keyboard/focus - not hover alone. --- ## Color & Contrast Color alone must never be the only way to convey information - always pair color with text, icons, or patterns. ### Contrast ratios (WCAG 2.2 AA) | Text type | Minimum ratio | |---|---| | Normal text (< 18pt / < 14pt bold) | 4.5:1 | | Large text (≥ 18pt / ≥ 14pt bold) | 3:1 | | UI components and graphics | 3:1 | | Decorative text, disabled UI | No requirement | ```html

    Email is required.

    ``` ### Warning: semi-transparent rgba() text Semi-transparent colors for text are unreliable for contrast compliance. The effective contrast of `rgba(R,G,B,0.5)` depends on every background layer beneath it, including parent elements, stacked components, and page backgrounds. You cannot evaluate the contrast of a semi-transparent color in isolation. **The problem:** `rgba(16,185,129,0.5)` (50% emerald-500) on white resolves to approximately `#87DCCA`, which has a contrast ratio around 1.9:1 against white. On a dark background the same token may pass. The color looks "present" visually but fails silently. **Rule:** Never use semi-transparent rgba() values for text content. Use fully opaque color tokens instead. If you need a "muted" appearance, pick a specific opaque value and measure its contrast directly. ```css /* Wrong: contrast is unpredictable across backgrounds */ color: rgba(16, 185, 129, 0.5); color: rgba(239, 68, 68, 0.5); /* Correct: fully opaque, measurable value */ color: #059669; /* emerald-600, 4.5:1 on white */ color: #dc2626; /* red-600, 5.9:1 on white */ ``` ### Context-dependent contrast: the same color token can pass or fail A color that passes contrast on one background will fail on another. This is the most common real-world failure pattern in multi-section layouts where sections use different background colors. **Example:** `slate-500` (#64748B) - On white (#FFFFFF): 4.48:1 - passes AA (barely) - On slate-50 (#F8FAFC): 4.17:1 - fails AA for normal text - On slate-100 (#F1F5F9): 3.86:1 - fails AA for normal text **Rule:** When a color token is used in more than one context, verify contrast against each background it appears on. Do not assume a token that passes on white will pass on tinted section backgrounds. ### Tailwind slate palette contrast reference Measured contrast ratios for slate shades against common backgrounds. AA requires 4.5:1 for normal text, 3:1 for large text and UI components. | Slate shade | Hex value | On white #FFFFFF | On slate-50 #F8FAFC | On slate-100 #F1F5F9 | |---|---|---|---|---| | slate-400 | #94A3B8 | 2.85:1 - FAIL | 2.65:1 - FAIL | 2.45:1 - FAIL | | slate-500 | #64748B | 4.48:1 - FAIL (AA) | 4.17:1 - FAIL | 3.86:1 - FAIL | | slate-600 | #475569 | 6.32:1 - PASS | 5.88:1 - PASS | 5.45:1 - PASS | | slate-700 | #334155 | 9.52:1 - PASS | 8.86:1 - PASS | 8.20:1 - PASS | | slate-800 | #1E293B | 13.3:1 - PASS | 12.4:1 - PASS | 11.5:1 - PASS | | slate-900 | #0F172A | 17.4:1 - PASS | 16.2:1 - PASS | 15.0:1 - PASS | **Key takeaways:** - `slate-400` and `slate-500` fail AA for normal text on all common backgrounds. Use them only for decorative, non-informational content or large text (3:1 threshold). - `slate-600` is the minimum safe value for normal text on white and light tinted backgrounds. - On slate-50 or slate-100 section backgrounds, be especially careful: the effective contrast is lower than on white by about 15-20%. ### Brand color accessibility: decorative vs. text tokens Bright brand colors are optimized for visual identity, not readability. Saturated greens, teals, and ambers that look bold and confident often fail AA contrast for normal-sized text, particularly on light backgrounds. **Example:** `emerald-500` (#10B981) - On white: 3.8:1 - FAIL for normal text (passes only for large text/UI components at 3:1) - On slate-100: approximately 3.5:1 - FAIL **Pattern: two tokens for one brand color** Define separate tokens for text use and decorative use. Never use the decorative/icon token on body-size text without checking contrast. ```css /* Token pair for a brand green */ --color-brand-decorative: #10B981; /* emerald-500: icons, borders, backgrounds, large headings */ --color-brand-text: #059669; /* emerald-600: inline text, small labels - 4.5:1 on white */ ``` ```tsx // Wrong: brand green on small text Status: Active {/* 3.8:1 on white - FAIL */} // Correct: darker shade for text use Status: Active {/* 4.5:1 on white - PASS */} // Decorative use (icon, badge background) - emerald-500 is fine here Active ``` Check every brand color at the size and background where it actually appears. "It looks bold" is not a contrast measurement. ### Tools - [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) - [Colour Contrast Analyser](https://www.tpgi.com/color-contrast-checker/) (desktop, eyedropper tool) - Chrome/Firefox DevTools color picker - shows contrast ratio inline --- ## Focus Management ### Visible focus indicator Never remove focus outlines without a replacement. WCAG 2.4.11 (new in 2.2) requires a minimum 2px perimeter area and 3:1 contrast between focused and unfocused states: ```css /* Wrong: removes indicator for everyone */ * { outline: none; } /* Correct: styled focus that meets 2.4.11 */ :focus-visible { outline: 3px solid #005fcc; outline-offset: 2px; border-radius: 2px; } ``` Use `:focus-visible` rather than `:focus` - it shows the indicator for keyboard users but suppresses it for mouse clicks, matching user expectations. ### Dialogs and modals When a dialog opens, move focus into it. When it closes, return focus to the trigger element. Trap focus within the dialog while it is open: ```javascript const trigger = document.getElementById('open-dialog-btn'); const dialog = document.getElementById('dialog'); const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; function openDialog() { dialog.removeAttribute('hidden'); dialog.querySelector(focusableSelectors).focus(); } function closeDialog() { dialog.setAttribute('hidden', ''); trigger.focus(); // Return focus to what opened the dialog } ``` ### Dynamic content When content updates dynamically (search results, filtered lists, notifications), announce it or move focus: ```javascript // Option A: announce via live region (no focus move - least disruptive) statusEl.textContent = `${results.length} results found.`; // aria-live="polite" on statusEl // Option B: move focus to the new content region resultsContainer.setAttribute('tabindex', '-1'); resultsContainer.focus(); ``` --- ## Motion & Animation Some users experience vestibular disorders triggered by motion. Always respect the OS-level reduced-motion preference. The `prefers-reduced-motion` media query and the 5-second animation threshold in WCAG 2.2.2 apply specifically to auto-playing, blinking, or scrolling content that runs in parallel with other content (carousels, looping animations, auto-advancing banners). Short CSS transitions (button hover states, accordion expand) are not subject to the 5-second rule. ```css /* Apply animations by default */ .animated { transition: transform 0.3s ease; } /* Remove or reduce motion when the user has requested it */ @media (prefers-reduced-motion: reduce) { .animated { transition: none; } } ``` Provide pause/stop controls for any auto-playing animation or carousel. Do not autoplay video or audio with sound. --- ## Testing No single tool catches everything. Use this order - each layer catches what the previous misses. ### 1. Automated scan Run first - fast and catches obvious violations. Automated tools catch approximately 30 - 40% of WCAG issues. - **axe DevTools** (browser extension) - WCAG violations, ARIA errors - **Lighthouse** (Chrome DevTools > Lighthouse tab) - accessibility score, summary of issues - **WAVE** (browser extension) - visual overlay showing errors, warnings, and structural info - **IBM Equal Access** (browser extension) - WCAG 2.2 coverage **Accessibility tree inspection:** Chrome DevTools > Elements panel > Accessibility tab shows the computed accessible name, role, and state for any element. Firefox has a dedicated Accessibility Inspector panel. Use these to debug unexpected announcements - more diagnostic than running axe alone. ### 2. Keyboard-only Tab through every interactive flow without a mouse: - Every interactive element must be reachable and operable - Focus indicator must always be visible - Test dialogs, dropdowns, date pickers, and all dynamic content - Verify Escape closes modals and dropdowns ### 3. Screen reader Updated pairings (as of 2026): | Screen reader | Platform | Browser | Free? | |---|---|---|---| | NVDA | Windows | Chrome (primary), Firefox | Yes | | JAWS | Windows | Chrome | No - most common enterprise SR | | Narrator | Windows | Edge | Yes (built-in) | | VoiceOver | macOS / iOS | Safari | Yes (built-in) | | TalkBack | Android | Chrome | Yes (built-in) | Basic test: navigate by headings (H in NVDA/JAWS), by landmarks (D), and by form elements (F). Verify every announcement makes sense without visual context. ### 4. Zoom - 200% - no content should overflow or become inaccessible - 400% - content must reflow to a single column without horizontal scrolling (WCAG 1.4.10 Reflow) ### 5. Text spacing Apply the WCAG 1.4.12 text spacing bookmarklet (sets `line-height: 1.5`, `letter-spacing: 0.12em`, `word-spacing: 0.16em`, `paragraph-spacing: 2em`). No content should overlap or be clipped. Bookmarklet source: [stevefaulkner's text spacing bookmarklet](https://www.html5accessibility.com/tests/tsbookmarklet.html) ### 6. Color and vision - Grayscale filter: Chrome DevTools > Rendering > Emulate vision deficiencies > Achromatopsia - verify no information is lost - Also test: Deuteranopia (red/green), Protanopia - Verify no information relies on color alone --- ## Anti-Patterns to Flag Flag these actively when encountered in code review or generation: - **Accessibility overlay scripts as sole compliance strategy** - overlays do not achieve WCAG conformance and have been challenged in ADA litigation. Mention this explicitly. - **`aria-label` that doesn't match visible button text** - breaks voice control (WCAG 2.5.3). The label must match or begin with the visible text so "click Submit" works. - **`aria-hidden="true"` on focusable elements** - creates a keyboard trap: the element receives focus but is invisible to screen readers. - **`tabindex` values greater than 0** - overrides the natural tab order and becomes unmanageable. - **Hover-only content with no keyboard/focus equivalent** - fails WCAG 2.1.1 and 1.4.13. - **Infinite scroll with no keyboard access to footer content** - users cannot reach content below the scroll boundary. Provide a "Load more" button or pagination. - **Custom date pickers with no keyboard support** - implement full arrow-key navigation or use a native ``. - **`aria-required="true"` on native ``** - redundant in modern browsers; keep only `required`. - **Placeholder text as the only label** - disappears when the user types; fails 1.3.1 and 2.4.6. - **Blocking clipboard paste in password fields** - breaks password managers and fails WCAG 3.3.8. - **Semi-transparent rgba() colors for text** - the effective contrast depends on all stacked backgrounds and cannot be measured from the color value alone. Use fully opaque tokens for any text content and measure the resulting rendered color against its background. - **Mid-gray palette values (slate-400, slate-500) on light section backgrounds** - slate-500 (#64748B) achieves only 4.48:1 on white (barely failing AA) and drops further on tinted backgrounds such as slate-50 (4.17:1) or slate-100 (3.86:1). Use slate-600 or darker for normal text on any light background. Flag all uses of slate-400 and slate-500 on text content for contrast verification. --- ## Checklist by Product Type ### All products (universal baseline) - [ ] `` set correctly; `lang` attribute on inline foreign-language content - [ ] Unique, descriptive `` on every page - [ ] One `<h1>` per page; no skipped heading levels - [ ] Landmarks used: `<main>`, `<nav>`, `<header>`, `<footer>`, `<aside>` - [ ] All `<img>` have `alt` (empty for decorative) - [ ] SVGs have `aria-label` or `aria-hidden` - [ ] Skip navigation link present and functional - [ ] All interactive elements reachable and operable by keyboard - [ ] Focus indicator visible and meets WCAG 2.4.11 (3px+ stroke, 3:1 contrast) - [ ] No `tabindex` values greater than 0 - [ ] `prefers-reduced-motion` respected - [ ] Normal text 4.5:1 contrast; large text and UI components 3:1 - [ ] No information conveyed by color alone - [ ] Automated scan (axe or Lighthouse) with no critical violations - [ ] Full keyboard navigation tested manually - [ ] Tested with at least one screen reader ### Online shop / e-commerce (add) - [ ] All product images have descriptive alt text - [ ] Form fields have `autocomplete` tokens (name, email, address, card details) - [ ] Multi-step checkout does not ask for repeated information (WCAG 3.3.7) - [ ] No CAPTCHA-only authentication - provide an accessible alternative (WCAG 3.3.8) - [ ] Touch targets on product cards and CTAs are at least 44×44px - [ ] Error recovery: clear error messages linked to specific fields ### Web app / SaaS (add) - [ ] Dialogs trap focus and return it on close - [ ] Dynamic content updates announced via `aria-live` or focus management - [ ] Help/support link in consistent location across all pages (WCAG 3.2.6) - [ ] Drag-and-drop interactions have a single-pointer alternative (WCAG 2.5.7) - [ ] Session timeout warning announced with enough time to extend ### Media / video site (add) - [ ] Captions on all video with speech or meaningful audio - [ ] Transcripts for audio-only content - [ ] Video player controls keyboard-operable - [ ] No autoplay with sound - [ ] Pause control for any looping animation or auto-advancing content ### Mobile / touch (add when mobile applies) - [ ] All touch targets at least 44×44px - [ ] `@media (pointer: coarse)` rules for touchscreen spacing - [ ] Hover-dependent content has keyboard/focus equivalent - [ ] Tested with TalkBack (Android) and VoiceOver (iOS) - [ ] Tested at 200% OS text size