`ToggleGroup` with `type="single"` announces `role="radiogroup"` but uses toolbar keyboard interaction
Summary
<ToggleGroup.Root type="single"> renders role="radiogroup" on the root and
role="radio" on the items, but arrow keys only move focus — they do not change
the selection. Per the WAI-ARIA APG radio group
pattern, arrow keys in a
radiogroup move focus and check the newly focused radio.
RadioGroup from the same library implements this correctly, which makes the
inconsistency easy to see side by side.
Why this is a mismatch and not a preference
The roles were added deliberately, in two steps, and neither discussion covered keyboard interaction:
- #951 — "[ToggleGroup] Use
role=radiowhentype=single" → PR #1118 - #3188 — "Toggle Group should have radiogroup role" → PR #3189
Both are closed and shipped. The result is that type="single" now carries the
roles of a radio group while keeping the roving-focus keyboard model of a
toolbar. Assistive technology announces "radio group, 1 of 3", and the
interaction that announcement implies is not there.
type="multiple" renders role="toolbar" and is unaffected — arrow-moves-only
is correct there. The mismatch exists only in the single case.
Reproduction
<ToggleGroup.Root type="single" defaultValue="a" aria-label="View">
<ToggleGroup.Item value="a">A</ToggleGroup.Item>
<ToggleGroup.Item value="b">B</ToggleGroup.Item>
</ToggleGroup.Root>Focus item A, press ArrowRight.
- Expected (APG radio group): focus moves to B and B becomes checked.
- Actual: focus moves to B, A stays checked. A second Space/Enter is needed.
Swapping the same markup to RadioGroup.Root / RadioGroup.Item gives the
expected behaviour.
A note on measuring this
Testing with a single press("ArrowRight") makes both primitives look
broken. press fires keydown and keyup back to back, and RadioGroup
clears on keyup the "arrow is down" flag that its onFocus handler reads.
Separating the events — keyboard.down, wait, keyboard.up — shows
RadioGroup checking and ToggleGroup not.
Versions
@radix-ui/react-toggle-group 1.1.19 · @radix-ui/react-radio-group 1.4.7 ·
Chromium via Playwright.
Source: radix-ui/primitives