Accessibility Standards (WCAG 2.2 AA)
Accessibility is a requirement, not a nice-to-have. All web and mobile frontends must meet WCAG 2.2 AA compliance.
Semantic HTML
- Use semantic elements:
<nav>, <main>, <article>, <section>, <aside>, <header>, <footer>
- Use heading hierarchy (
h1-h6) — one h1 per page, no skipped levels
- Use
<button> for actions, <a> for navigation — never <div onClick>
- Use
<ul>/<ol> for lists, <table> for tabular data
Keyboard Navigation
- All interactive elements must be keyboard accessible (Tab, Enter, Space, Escape)
- Visible focus indicators on all focusable elements — never
outline: none without a replacement
- Logical tab order following visual layout
- Skip-to-content link as first focusable element
- Trap focus inside modals and dialogs — release on close
Color and Contrast
- Minimum contrast ratio: 4.5:1 for normal text, 3:1 for large text (18px+ or 14px+ bold)
- Never convey information by color alone — use icons, patterns, or text labels
- Test with grayscale filter to verify non-color cues exist
Forms
- Every input must have an associated
<label> (use htmlFor/id pairing or wrapping)
- Error messages must be programmatically associated with inputs (
aria-describedby)
- Required fields marked with both visual indicator and
aria-required="true"
- Group related fields with
<fieldset> and <legend>
Images and Media
- All
<img> elements must have alt text — descriptive for informational, empty (alt="") for decorative
- Use
next/image (Next.js) or optimized <img> (Vite) with alt attribute
- Video content must have captions or transcripts
ARIA
- Use ARIA only when native HTML semantics are insufficient
aria-label for elements without visible text (icon buttons)
aria-live regions for dynamic content updates (toast notifications, form errors)
aria-expanded for collapsible sections and dropdowns
- Never use
aria-hidden="true" on focusable elements
Component Patterns
// Accessible button with icon only
<button aria-label="Delete order"
<TrashIcon aria-hidden="true" />
</button>
// Accessible form field
<div>
<label htmlFor="email">Email Address</label>
<input id="email" type="email" aria-required="true" aria-describedby="email-error" />
{error && <p id="email-error" role="alert">{error}</p>}
</div>
// Live region for dynamic updates
<div aria-live="polite" aria-atomic="true">
{`${items.length} items in cart`}
</div>
WCAG 2.2 New Criteria
WCAG 2.2 adds 9 success criteria beyond WCAG 2.1 and removes 4.1.1 Parsing (obsolete —
do not audit for it). Six of the nine bind at AA: 3.2.6 and 3.3.7 (Level A, which AA
includes) plus 2.4.11, 2.5.7, 2.5.8, 3.3.8 (Level AA). The other three —
2.4.12 Focus Not Obscured (Enhanced), 2.4.13 Focus Appearance, and 3.3.9 Accessible
Authentication (Enhanced) — are AAA.
We adopt one AAA criterion as a house rule: 2.4.13 Focus Appearance. Labelled honestly
below so nobody mistakes our choice for the standard's requirement — it is stricter than AA, we
do it on purpose, and the design system already ships the ring that satisfies it.
2.4.11 Focus Not Obscured (Minimum) (AA)
- When a component receives focus, it must not be entirely hidden by author-created content (sticky headers, floating toolbars, cookie banners)
- Ensure sticky/fixed elements don't cover focused items; use
scroll-margin-top to offset
2.4.13 Focus Appearance (AAA — house rule, not required for AA)
- Focus indicator must be at least 2px thick (outline or ring)
- Focus indicator must have at least 3:1 contrast against the unfocused state
- Standard pattern:
focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
2.5.7 Dragging Movements (AA)
- Any operation achievable by dragging must have a single-pointer alternative
- Examples: drag-to-reorder must have up/down buttons; drag-to-resize must have input fields
- Exception: dragging is essential to the functionality (e.g., drawing tool)
2.5.8 Target Size (Minimum) (AA)
- Interactive targets must be at least 24x24 CSS pixels
- Mobile touch targets should be at least 44x44px (WCAG recommendation)
- Exceptions: inline links in text, spacing between targets provides equivalent area
- Use
min-h-[44px] min-w-[44px] for mobile touch targets
3.2.6 Consistent Help (A)
- Help mechanisms (chat, FAQ, contact) must appear in the same relative location across pages
- If a help button is in the footer on one page, it must be in the footer on all pages
3.3.7 Redundant Entry (A)
- Information previously entered by the user must be auto-populated or available for selection
- Don't ask users to re-enter data already provided in the same process
- Examples: shipping address auto-fills billing; previously entered email shown in confirmation
3.3.8 Accessible Authentication (Minimum) (AA)
- Authentication must not require a cognitive function test (e.g., remembering a password)
- Allow password managers to fill credentials (no blocking paste in password fields)
- CAPTCHAs must have accessible alternatives
- Biometric and WebAuthn are acceptable alternatives
Testing
- Use
axe-core or jest-axe for automated accessibility testing in Vitest
- Manual keyboard testing for all new interactive components
- Test with screen reader (VoiceOver on macOS, NVDA on Windows)
- Verify focus management on route changes and modal open/close
- Validate touch target sizes on mobile (44x44px minimum)
- Verify focus indicators are not obscured by sticky/fixed elements
1---2name: std-accessibility3description: Accessibility standards (WCAG 2.2 AA) — semantic HTML, keyboard nav, color contrast, ARIA, focus, target size. Use when building or reviewing web UI components.4---56# Accessibility Standards (WCAG 2.2 AA)78Accessibility is a requirement, not a nice-to-have. All web and mobile frontends must meet WCAG 2.2 AA compliance.910## Semantic HTML1112- Use semantic elements: `<nav>`, `<main>`, `<article>`, `<section>`, `<aside>`, `<header>`, `<footer>`13- Use heading hierarchy (`h1`-`h6`) — one `h1` per page, no skipped levels14- Use `<button>` for actions, `<a>` for navigation — never `<div onClick>`15- Use `<ul>`/`<ol>` for lists, `<table>` for tabular data1617## Keyboard Navigation1819- All interactive elements must be keyboard accessible (Tab, Enter, Space, Escape)20- Visible focus indicators on all focusable elements — never `outline: none` without a replacement21- Logical tab order following visual layout22- Skip-to-content link as first focusable element23- Trap focus inside modals and dialogs — release on close2425## Color and Contrast2627- Minimum contrast ratio: 4.5:1 for normal text, 3:1 for large text (18px+ or 14px+ bold)28- Never convey information by color alone — use icons, patterns, or text labels29- Test with grayscale filter to verify non-color cues exist3031## Forms3233- Every input must have an associated `<label>` (use `htmlFor`/`id` pairing or wrapping)34- Error messages must be programmatically associated with inputs (`aria-describedby`)35- Required fields marked with both visual indicator and `aria-required="true"`36- Group related fields with `<fieldset>` and `<legend>`3738## Images and Media3940- All `<img>` elements must have `alt` text — descriptive for informational, empty (`alt=""`) for decorative41- Use `next/image` (Next.js) or optimized `<img>` (Vite) with `alt` attribute42- Video content must have captions or transcripts4344## ARIA4546- Use ARIA only when native HTML semantics are insufficient47- `aria-label` for elements without visible text (icon buttons)48- `aria-live` regions for dynamic content updates (toast notifications, form errors)49- `aria-expanded` for collapsible sections and dropdowns50- Never use `aria-hidden="true"` on focusable elements5152## Component Patterns5354```tsx55// Accessible button with icon only56<button aria-label="Delete order" onClick={handleDelete}>57 <TrashIcon aria-hidden="true" />58</button>5960// Accessible form field61<div>62 <label htmlFor="email">Email Address</label>63 <input id="email" type="email" aria-required="true" aria-describedby="email-error" />64 {error && <p id="email-error" role="alert">{error}</p>}65</div>6667// Live region for dynamic updates68<div aria-live="polite" aria-atomic="true">69 {`${items.length} items in cart`}70</div>71```7273## WCAG 2.2 New Criteria7475WCAG 2.2 adds **9** success criteria beyond WCAG 2.1 and **removes 4.1.1 Parsing** (obsolete —76do not audit for it). Six of the nine bind at AA: `3.2.6` and `3.3.7` (Level A, which AA77includes) plus `2.4.11`, `2.5.7`, `2.5.8`, `3.3.8` (Level AA). The other three —78`2.4.12` Focus Not Obscured (Enhanced), `2.4.13` Focus Appearance, and `3.3.9` Accessible79Authentication (Enhanced) — are **AAA**.8081**We adopt one AAA criterion as a house rule: `2.4.13` Focus Appearance.** Labelled honestly82below so nobody mistakes our choice for the standard's requirement — it is stricter than AA, we83do it on purpose, and the design system already ships the ring that satisfies it.8485### 2.4.11 Focus Not Obscured (Minimum) (AA)86- When a component receives focus, it must not be entirely hidden by author-created content (sticky headers, floating toolbars, cookie banners)87- Ensure sticky/fixed elements don't cover focused items; use `scroll-margin-top` to offset8889### 2.4.13 Focus Appearance (**AAA** — house rule, not required for AA)90- Focus indicator must be at least **2px thick** (outline or ring)91- Focus indicator must have at least **3:1 contrast** against the unfocused state92- Standard pattern: `focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2`9394### 2.5.7 Dragging Movements (AA)95- Any operation achievable by dragging must have a single-pointer alternative96- Examples: drag-to-reorder must have up/down buttons; drag-to-resize must have input fields97- Exception: dragging is essential to the functionality (e.g., drawing tool)9899### 2.5.8 Target Size (Minimum) (AA)100- Interactive targets must be at least **24x24 CSS pixels**101- Mobile touch targets should be at least **44x44px** (WCAG recommendation)102- Exceptions: inline links in text, spacing between targets provides equivalent area103- Use `min-h-[44px] min-w-[44px]` for mobile touch targets104105### 3.2.6 Consistent Help (A)106- Help mechanisms (chat, FAQ, contact) must appear in the same relative location across pages107- If a help button is in the footer on one page, it must be in the footer on all pages108109### 3.3.7 Redundant Entry (A)110- Information previously entered by the user must be auto-populated or available for selection111- Don't ask users to re-enter data already provided in the same process112- Examples: shipping address auto-fills billing; previously entered email shown in confirmation113114### 3.3.8 Accessible Authentication (Minimum) (AA)115- Authentication must not require a cognitive function test (e.g., remembering a password)116- Allow password managers to fill credentials (no blocking paste in password fields)117- CAPTCHAs must have accessible alternatives118- Biometric and WebAuthn are acceptable alternatives119120## Testing121122- Use `axe-core` or `jest-axe` for automated accessibility testing in Vitest123- Manual keyboard testing for all new interactive components124- Test with screen reader (VoiceOver on macOS, NVDA on Windows)125- Verify focus management on route changes and modal open/close126- Validate touch target sizes on mobile (44x44px minimum)127- Verify focus indicators are not obscured by sticky/fixed elements