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
Claude wrote a React DatePicker, but making it WCAG-accessible took another 3 days
Source: Habr AI. Collage: Hamidun News.
Listen to article
Article Fact Sheet & Key Figures
ParameterDetails
Primary sourceHabr AI ↗
Editor-in-Chief
The focus trap did not workwhen tabbing inside the dialog, focus moved out to the main document
Dates were announced incorrectlythe screen reader read "1" instead of "Monday, June 1, 2026"
Month changes were silentthe `aria-live` region did not update when the month changed

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.

Story

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

ZK
Zhemal Khamidun — CPO at Alpina Digital, product manager of AlpinaGPT. Building AI products since 2019. Hamidun News is a pet project that grew into a production aggregator with 6,000+ readers.
✦ Unified AI Workspace · Nomad

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.

Try Nomad AI →Launch Nomad Agent
What do you think?
Loading comments…