Accessibility in React that actually ships: a WCAG 2.2 field guide
Automated tools catch 30% of issues. The other 70% need keyboard testing, screen reader runs, and deliberate ARIA patterns. Here's what production-ready a11y looks like.
Accessibility tooling has improved significantly in 2026 — axe-core is built into Chrome DevTools, CI pipelines run automated a11y audits, and component libraries like Radix UI handle the hardest interaction patterns out of the box. But automated tools catch roughly 30% of real issues. The 70% that scanners miss — logical heading order, meaningful link text, focus management after async updates — requires deliberate effort at the component level.
The automated baseline
Start with axe-core or @axe-core/react in development — it surfaces easy wins automatically (missing alt text, empty buttons, insufficient colour contrast). Add eslint-plugin-jsx-a11y to catch issues at write time. These two tools together take under an hour to set up and prevent the most common failures. They are the floor, not the ceiling.
// Icon-only button — axe and eslint-plugin-jsx-a11y will catch this
<button onClick={close}>
<XIcon />
</button>
// Correct: aria-label provides the accessible name
// aria-hidden on the icon prevents screen readers from announcing the SVG
<button onClick={close} aria-label="Close dialog">
<XIcon aria-hidden="true" />
</button>Focus management: the most common React-specific failure
SPAs break the browser's default focus management — navigation doesn't move focus to new page content, modal close doesn't restore focus to the trigger, and async content loads silently. These aren't caught by axe. The patterns to implement: move focus to the page h1 after route changes, return focus to the trigger element when a modal closes, and announce dynamic content changes with aria-live regions.
WCAG 2.2 additions that affect common patterns
WCAG 2.2 added criteria that affect everyday UI work. The most practically impactful: 2.5.3 (Target Size Minimum) requires interactive targets to be at least 24x24 CSS pixels — catches small icon buttons. 3.2.6 (Consistent Help) requires help mechanisms to appear in the same relative location across pages. 2.4.11 (Focus Not Obscured) means sticky headers can't fully hide a focused element.
Testing that finds what scanners miss
- Test with VoiceOver on Mac (Cmd+F5) and NVDA on Windows — they behave differently and both have significant user bases.
- Test keyboard-only navigation: Tab, Shift+Tab, Enter, Space, and arrow keys. Every interactive element must be reachable without a mouse.
- Use Chrome's Accessibility panel (DevTools > More tools > Accessibility) to inspect the accessibility tree for custom components.
- Include one keyboard-only and one screen reader run in every feature's manual QA checklist — automated scans won't find the issues that matter most to real users.
Written by Appesto Engineering.