Skip to content

Make import candidate selection a native radiogroup - #136

Merged
AdamXweb merged 1 commit into
mainfrom
fix/candidate-row-semantics
Jul 23, 2026
Merged

Make import candidate selection a native radiogroup#136
AdamXweb merged 1 commit into
mainfrom
fix/candidate-row-semantics

Conversation

@adamXbot

Copy link
Copy Markdown
Collaborator

Summary

The iTunes match candidates in the onboarding wizard's SearchResultBlock were bare clickable <div>s — no role, no tabIndex, no keyboard path, invisible to assistive tech. This converts them to <button role="radio"> rows inside a labelled role="radiogroup", reusing the wizard's existing roving-radiogroup pattern (lib/use-roving-radiogroup.ts, the same one the method and AI-provider cards use):

  • Tab enters the group on the chosen row (first row when nothing is chosen, via rovingTabIndex); arrows move with selection-follows-focus; Home/End and RTL flipping come with the shared hook.
  • Click/Space on the already-chosen row still clears it — existing toggle behaviour preserved; the hook's checked guard keeps arrow passes from clearing rows in transit.
  • The group's aria-label carries the query (new i18n key onboard.search_block.candidates_group_aria, en + zh).
  • The decorative checkmark span is aria-hidden and the candidate icon's alt is now empty, so the row's accessible name is the candidate name/developer text rather than the name read twice.
  • .candidate-row CSS neutralises the UA button chrome — visual layout is unchanged.

Test changes

The fixme-gated candidate-selection spec in tests/e2e/onboarding-keyboard.spec.ts — committed with the a11y gate (#134) as this fix's acceptance test — is now live: it walks the wizard keyboard-only, steps into the radiogroup with Shift+Tab, and selects the non-default Apple candidate with ArrowDown, asserting focus, aria-checked, and the chosen state.

Verification

  • Full Playwright suite: 34 passed / 0 skipped (the suite's one standing skip was this spec) — includes the axe scan of the match step with the new markup and all click-based candidate flows in onboard-import.spec.ts.
  • 438 unit tests, 0 failures; pnpm typecheck, pnpm lint, pnpm lint:i18n (4,760 keys at parity) all clean.

🤖 Generated with Claude Code

The iTunes match candidates in SearchResultBlock were bare clickable
<div>s — no role, no tabIndex, no keyboard path, nothing exposed to
assistive tech. They are now <button role="radio"> rows inside a
labelled role="radiogroup" using the wizard's existing roving
pattern (lib/use-roving-radiogroup.ts): Tab enters the group on the
chosen row (first row when nothing is chosen, via rovingTabIndex),
arrows move with selection-follows-focus, and clicking/Space on the
chosen row still clears it — the hook's checked guard keeps arrow
passes from clearing rows on the way through.

Details:
- group aria-label carries the query (new i18n key
  onboard.search_block.candidates_group_aria, en + zh)
- the decorative checkmark span is aria-hidden and the candidate icon
  alt is empty — the row's accessible name is the candidate name/dev
  text, not repeated image alt
- .candidate-row CSS neutralises UA button chrome; layout unchanged
- the fixme-gated keyboard spec in onboarding-keyboard.spec.ts is now
  live and guards the arrow-selection contract (Shift+Tab into the
  group, ArrowDown selects the alternate candidate)

Verified: full Playwright suite 34 passed / 0 skipped (axe scan of
the match step, keyboard specs, and all click-based candidate flows);
438 unit tests; typecheck, lint, and locale parity clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AdamXweb
AdamXweb merged commit c5d6ffb into main Jul 23, 2026
13 checks passed
@AdamXweb
AdamXweb deleted the fix/candidate-row-semantics branch July 23, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants