Claude wrote a React DatePicker, but making it WCAG-accessible took another 3 days
Claude generated a React DatePicker with ARIA attributes and keyboard navigation in minutes. But once the team ran NVDA and VoiceOver, serious problems surfaced: focus escaped the dialog, dates were read out incorrectly, Escape left users without context, and high-contrast mode broke the visual states. Real WCAG accessibility took another three days of manual work.
AI-processed from Habr AI; edited by Hamidun News
A development team asked Claude to build a custom React DatePicker for a doctor appointment form, with full keyboard navigation and screen reader support. The AI produced a first version quickly. Then came three days of rework.
What Claude did well
Claude generated a working foundation for the component: the JSX structure, basic ARIA attributes (`role="dialog"`, `aria-label`, `aria-live`), key handlers for arrow-key navigation, and the calendar grid logic. As a starting point it was good: the code compiled, looked right visually, and manual checks broke nothing.
The AI was especially useful with boilerplate: generating the 42 cells of the grid, calculating the offset of the first day of the month, and formatting dates for `aria-label`. This is tedious work to do by hand, and Claude handled it cleanly.
The problem only became obvious once real assistive technologies were connected.
Where it all went wrong
The real testing began when the team ran NVDA on Windows and VoiceOver on macOS. Several problems turned up:
- The focus trap did not work: when tabbing inside the dialog, focus moved out to the main document
- Dates were announced incorrectly: the screen reader read "1" instead of "Monday, June 1, 2026"
- Month changes were silent: the `aria-live` region did not update when the month changed
- Escape closed the calendar but did not return focus: after closing, focus was lost instead of going back to the trigger button
- Windows High Contrast: the selected date looked the same as the others, because the component relied on CSS color without state attributes
Each of these items looked like "there is a handler" in the generated code, but behavior with a real screen reader differed from what was expected.
How they fixed it
The fix was the WAI-ARIA APG, the official W3C guide with a concrete pattern for a DatePicker dialog. It does not just say "add `aria-modal`". It spells out the exact sequence: which element receives focus on open, how to maintain the focus trap, where to return focus on close, and which `aria-label` values the month navigation buttons need.
"Reading generated code is not the same as checking accessibility.
Only a real screen reader shows the truth."
The team rewrote the focus trap using `querySelectorAll`, with manual interception of Tab and Shift+Tab. Each day cell got a full `aria-label` such as "Wednesday, June 3, 2026", plus `aria-pressed="true"` for the selected state. For high-contrast mode, they added `aria-selected` and an `outline` border instead of relying on background color alone.
The trickiest part was `aria-live`: the region must exist in the DOM before text is written into it, otherwise the screen reader ignores it. Claude inserted the region dynamically, and this nuance only came to light in testing.
What this means
Claude speeds up writing accessible code, but it does not replace testing with screen readers. The good news: once a team has worked through the WAI-ARIA APG and documented its decisions, the next accessible component gets written noticeably faster. The AI takes on the boilerplate, but the final check with NVDA and VoiceOver cannot be skipped.
- Nov 1, 2026Kimi scores 96.2% on a 1C database analytics test and emerges as a fallback if Claude access is blocked
- Nov 1, 2026OpenAI Rewrites GPT Behavior Rules: Five Values and a Chain of Command
- Nov 1, 2026Google admits AI Mode cuts search revenue, shares fall 15%
- Oct 31, 2026Author puts his script before an AI jury: the verdict cost 850 thousand tokens
Key Questions Answered
What Claude did well?
Claude generated a working foundation for the component: the JSX structure, basic ARIA attributes (`role="dialog"`, `aria-label`, `aria-live`), key handlers for arrow-key navigation, and the calendar grid logic. As a starting point it was good: the code compiled, looked right visually, and manual checks broke nothing. The AI was especially useful with boilerplate: generating the 42 cells of the grid, calculating the offset of the first day of the month, and formatting dates for `aria-label`. This is tedious work to do by hand, and Claude handled it cleanly. The problem only
Where it all went wrong?
The real testing began when the team ran NVDA on Windows and VoiceOver on macOS. Several problems turned up: The focus trap did not work: when tabbing inside the dialog, focus moved out to the main document Dates were announced incorrectly: the screen reader read "1" instead of "Monday, June 1, 2026" Month changes were silent: the `aria-live` region did not update when the month changed Escape closed the calendar but did not return focus: after closing, focus was lost instead of going back to the trigger button Windows High Contrast: the selected date looked the same as the
How they fixed it?
The fix was the WAI-ARIA APG, the official W3C guide with a concrete pattern for a DatePicker dialog. It does not just say "add `aria-modal`". It spells out the exact sequence: which element receives focus on open, how to maintain the focus trap, where to return focus on close, and which `aria-label` values the month navigation buttons need. "Reading generated code is not the same as checking accessibility. Only a real screen reader shows the truth." The team rewrote the focus trap using `querySelectorAll`, with manual interception of Tab and Shift+Tab. Each day cell got a
Test Frontier LLMs & Autonomous Agents in One Workspace
Run Claude Opus 5.5, GPT-6, Gemini 3.1, and DeepSeek side-by-side in Nomad AI (nomadnet.ai) — unified model routing, deep research, and multimodal workflows under one subscription.
The AI world, distilled — once a week
Seven stories that actually mattered, hand-picked. No noise, no reposts, no press releases.
Done! Check your inbox for a confirmation.