Radio
Radio — exactly one choice from a short set.
Beta · Forms · packages/css/src/components/radio.css
.pui-radio · .pui-radio-group (a fieldset, gap space/12; --horizontal).
Sizes across, states down.
What it is for
Section titled “What it is for”- Checked: false, true — once the user has chosen, one item in the group is always true
- Five states: default, hover, focus, error, disabled — the same rules as Checkbox; error marks the border, the dot stays
- Label is a text property and can be hidden (Has label) inside an option card: a selected card is blue/100 with a blue/400 stroke
- Radios travel in a group: stack them with the group’s gap (12) inside a fieldset with a legend — the Field family’s next row
Use when exactly one option from two to about six, all visible at once.
Do not use when more than one may be picked (Checkbox); the set is long or searchable (Select, Combobox); the choice is on/off (Switch); a single radio on its own — it cannot be un-chosen.
Relates to Checkbox, Switch, Select, Field, Toggle group (the same choice as a segmented control).
Anti-patterns a default nobody chose when the answer matters · a lone radio · radios in a row with wrapped labels · a group without a legend.
Keywords: radio, radio group, single choice, option, select one
Figma — page Checkbox, Radio & Switch: Radio (10 variants)
shadcn: radio-group
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens radio.css.
<fieldset class="pui-radio-group"><legend class="pui-label">Billing</legend> <label class="pui-radio"><input class="pui-radio__input pui-sr-only" type="radio" name="billing"> <span class="pui-radio__box"></span><span class="pui-radio__label">Monthly</span></label> …</fieldset>The modifiers
Section titled “The modifiers”| What it is | Note | |
|---|---|---|
| Group | .pui-radio-group — a fieldset with a legend, items stacked at space/12 | — |
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”| ink on ground | reads | floor | the matrix calls it | |
|---|---|---|---|---|
#0b0202 on #ffffff |
20.51 | 4.5 | text.default on surface.page (promised) | ok |
1 distinct pair(s) rendered, 0 that axe calls a violation.
The focus ring
Section titled “The focus ring”2 of 2 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured; 4 stop(s) the Tab key does not reach or that the specimen draws in a state that only exists while focused.
Keyboard
Section titled “Keyboard”Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.