Skip to main content
Reference

Accessibility standard

WCAG 2.1 AA for all UI work. Semantic HTML first, ARIA only when needed. Keyboard-navigate everything. Test with axe-core + real screen reader.

Accessibility standard

Purpose: Defines WCAG 2.1 Level AA accessibility requirements for all framework child projects. Serves as a developer quick-reference for color contrast, keyboard navigation, semantic HTML, ARIA patterns, forms, and testing.

This standard defines accessibility requirements for all framework child projects. It is a developer quick-reference — for deep audits, invoke the Accessibility Auditor agent.

Target: WCAG 2.1 Level AA conformance.

Color contrast

Element Minimum Ratio Example
Normal text (< 18px / 14px bold) 4.5:1 Body copy, labels, links
Large text (≥ 18px / 14px bold) 3:1 Headings, large buttons
UI components & graphical objects 3:1 Borders, icons, focus rings

Use the browser DevTools contrast checker or npx @axe-core/cli to validate. All 43 framework themes must pass these ratios.

Keyboard navigation

Every interactive element must be operable with keyboard alone:

  • Tab — Moves focus forward through interactive elements in logical order
  • Shift+Tab — Moves focus backward
  • Enter/Space — Activates buttons and links
  • Escape — Closes modals, dropdowns, overlays; focus returns to trigger
  • Arrow keys — Navigate within composite widgets (tabs, menus, radio groups)
  • No keyboard traps — Users can always Tab away from any element
  • Visible focus indicator — On every focusable element (never outline: none without a replacement)

Focus management for dynamic content

  • Modals — Trap focus inside. On close, return focus to the element that opened it.
  • Route changes (SPA) — Announce the new page title. Move focus to the main content area or <h1>.
  • Toast/notifications — Use aria-live="polite" so screen readers announce without stealing focus.
  • Delete/remove actions — Move focus to the nearest logical element after the removed item.

Semantic HTML first

Use native HTML elements before reaching for ARIA:

Instead of Use
<div role="button" tabindex="0"> <button>
<div role="link"> <a href="...">
<div role="heading" aria-level="2"> <h2>
<span role="checkbox"> <input type="checkbox">

The best ARIA is the ARIA you don't need. Radix UI primitives (used by shadcn/ui) handle most ARIA patterns correctly — don't override their defaults unless you have a specific reason.

Common ARIA patterns

When native HTML isn't sufficient:

  • aria-label — Labels for elements without visible text (icon buttons, search inputs)
  • aria-describedby — Associates error messages with form fields
  • aria-live="polite" — Announces dynamic content changes (toasts, status updates)
  • aria-expanded — Indicates open/closed state (accordions, dropdowns)
  • aria-hidden="true" — Hides decorative content from screen readers
    • Never put on focusable elements

ARIA anti-patterns (do NOT)

  • No aria-label on non-interactive elements — Don't apply to <div> or <span>
  • No role swapping — Don't add role="button" to <a> or vice versa; use the correct element
  • No aria-hidden="true" with focusable children — Hides the container but leaves children reachable
  • No tabindex > 0 — Breaks natural tab order

Forms

  • Labels — Every <input>, <select>, <textarea> must have an associated <label> (via htmlFor/id or wrapping)
  • Required fields — Use aria-required="true" and visual indicator
  • Error messages — Associate with aria-describedby pointing to the error element
  • Field grouping — Group related fields with <fieldset> and <legend> (radio groups, address blocks)
  • Validation errors — React Hook Form + Zod errors should be announced
    • Use aria-live on the error container or aria-describedby on the input

Images and media

  • Informative images — Descriptive alt text
  • Decorative imagesalt="" (empty string, not missing)
  • Complex images — Charts, diagrams: alt summary + longer description via aria-describedby
  • Icons as buttonsaria-label on the button, aria-hidden="true" on the icon SVG

Testing checklist

Run before every PR that touches UI:

# Automated scan (catches ~30% of issues)
npx @axe-core/cli http://localhost:3000 --tags wcag2a,wcag2aa

Manual checks (catches the other 70%):

  • Tab through the entire page — can you reach everything? Logical order?
  • Activate every button/link with Enter or Space
  • Open and close every modal/dropdown with keyboard
  • Check focus visibility — can you always see where you are?
  • Test with VoiceOver (macOS: Cmd+F5) — do headings, landmarks, and labels make sense?
  • Zoom to 200% — does layout reflow without horizontal scrolling?
  • Enable prefers-reduced-motion — do animations respect it?

For comprehensive audits, invoke the Accessibility Auditor agent.

See also

  • standards/core/review-protocol.md — Accessibility in code review
  • Standards agent — Standards compliance checks including accessibility (.claude/agents/standards.md)
  • ui-ux-pro-max skill — Quick accessibility rules for design work

Search Framework Explorer

Search agents, skills, and standards