#4119·primitives

`ToggleGroup` with `type="single"` announces `role="radiogroup"` but uses toolbar keyboard interaction

Author: lucasmatosdeoliveiraCreated Aug 17, 2026Updated Aug 17, 2026

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=radio when type=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

typescript
<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.