Source: PRD-018 SC-2 audit, finding [Logic-C1].
template/src/widgets/health-bar/ui/HealthBar.svelte:93-106
The pre-migration theme switcher used role="radiogroup" with three role="radio" items (AUTO / LIGHT / DARK) carrying explicit aria-checked. After migration to <ToggleGroup type="single">, bits-ui renders these as a generic toggle group rather than a radio group. Screen readers will announce "toggle button group" instead of "radio group, 1 of 3 selected".
This is a silent a11y regression versus the pre-migration code.
Repro / Verification
- Run
npm run dev in template/.
- Navigate to HealthBar's theme switcher.
- Inspect with
Accessibility tab in Chrome DevTools — the radiogroup role should be present on the wrapper, radio on each item.
Fix options
- (preferred) Wrap the ToggleGroup in
role="radiogroup" + aria-label="Theme", then map each ToggleGroupItem to render with role="radio" aria-checked={...} when its parent is type="single". This requires a small extension of the shared/ui/toggle-group/ primitive (see RFC-016).
- (workaround) Replace
ToggleGroup here with three explicit Toggle primitives wired together as a manual radiogroup. Loses the bits-ui keyboard-navigation freebies.
Acceptance
- DevTools Accessibility tree shows
radiogroup on the wrapper and radio on each item.
- Keyboard: arrow keys move selection,
Tab exits the group (roving tabindex preserved).
- VoiceOver / NVDA announce "radio button" for each option.
Audit traceability
- Reviewer: Logic & Correctness, score 7/10
- File:
template/src/widgets/health-bar/ui/HealthBar.svelte
Source: PRD-018 SC-2 audit, finding [Logic-C1].
template/src/widgets/health-bar/ui/HealthBar.svelte:93-106The pre-migration theme switcher used
role="radiogroup"with threerole="radio"items (AUTO/LIGHT/DARK) carrying explicitaria-checked. After migration to<ToggleGroup type="single">, bits-ui renders these as a generic toggle group rather than a radio group. Screen readers will announce "toggle button group" instead of "radio group, 1 of 3 selected".This is a silent a11y regression versus the pre-migration code.
Repro / Verification
npm run devintemplate/.Accessibilitytab in Chrome DevTools — theradiogrouprole should be present on the wrapper,radioon each item.Fix options
role="radiogroup"+aria-label="Theme", then map eachToggleGroupItemto render withrole="radio" aria-checked={...}when its parent istype="single". This requires a small extension of theshared/ui/toggle-group/primitive (seeRFC-016).ToggleGrouphere with three explicitToggleprimitives wired together as a manual radiogroup. Loses the bits-ui keyboard-navigation freebies.Acceptance
radiogroupon the wrapper andradioon each item.Tabexits the group (roving tabindex preserved).Audit traceability
template/src/widgets/health-bar/ui/HealthBar.svelte