One selected value from a related set — mutually exclusive. Group radios by a shared name; exactly one is chosen. For independent on/off choices use a checkbox instead.
Real DOM drivers set every state; demo classes force hover and focus here. One size — size.icon.md. Unselected hover previews the primary dot on the unfilled ring; selection reverses it onto the primary fill, which remains filled as it steps to its hover color.
| Example | State | Driver | Roles | Usage |
|---|---|---|---|---|
| Rest, unselected | — | border.strongsurface.page | An option in a set, not chosen | |
| Hover | :hover | surface.pageaction.primary.bg | The primary dot previews selection on the unfilled ring | |
| Selected | :checked | action.primary.bg.text | The dot marks the one chosen value | |
| Selected + hover | :checked:hover | action.primary.hover.text | The fill responds without disguising the selected value | |
| Focus | :focus-visible | focus.ring | Keyboard focus — the shared ring | |
| Invalid | [aria-invalid] | status.error.border | Flag the group, then reference the message | |
| Disabled | :disabled | action.disabled.bgborder.default | Visible but not selectable |
Radios share a name and one native fieldset legend. The group-level invalid state colors the boundary and legend, owns its wrapping error, and clears all three when an enabled option is chosen.
Radios are one-of-many — the set shares a name and a single answer.
| Verdict | Rule |
|---|---|
| Do | Group radios with a shared name in fieldset.ds-choice-group under a real legend; pre-select a sensible default when one exists. |
| Do | Give every option a label — visible via its row, or aria-label when the context is the label. |
| Do | Validate the group, not the option: set aria-invalid and reference the message via aria-describedby. |
| Don’t | Stretch a radio set past what fits visibly — longer lists collapse into a Select. |
| Don’t | Use radios for independent on/off choices — that is Checkbox — or a horizontal one-of-few — that is SegmentedControl. |