RadioGroup
Picks exactly one option from a short, visible set. The group has a legend, each option a label, and the whole group one error message.
Basic usage
Install the package:
pnpm add @design-schema/react @design-schema/tokens Import the stylesheet once for the app, then the component:
import '@design-schema/tokens/calm-precise/css';
import '@design-schema/react/index.css';
import { RadioGroup } from '@design-schema/react'; Examples
import { RadioGroup } from '@design-schema/react';
<RadioGroup
label="Shipping method"
name="shipping"
options={[
{ value: 'standard', label: 'Standard', description: 'Free, 3 to 5 business days' },
{ value: 'express', label: 'Express', description: 'Next business day' },
{ value: 'pickup', label: 'Pick up in store', description: 'Ready in 2 hours' },
]}
/>Props
11 rows| Prop | Type | Default | Description |
|---|---|---|---|
| label required | string | — | The group's legend — the question the options answer. Always visible. Accessibility: Rendered as the fieldset legend on web; as the group's accessibilityLabel on native. |
| name required | string | — | Field name used by the enclosing Form. Also links the radios into one native group on web. |
| options required | array { value: string; label: string; description?: string; disabled?: boolean }[] | — | The options in display order. Two to about seven; more than that is a Select (planned). Values are short identifiers (letters, digits, dashes) — they become element ids. Export the item type as `RadioGroupOption`. |
| value | string | — | Controlled selected value. Omit for an uncontrolled group. |
| defaultValue | string | — | Initial selection for an uncontrolled group. Omit to start with nothing selected. It is read once, when the group first renders: assigning it later — routine on a Lit element, whose properties are often set after upgrade — is a no-op, exactly as a React state initialiser is. |
| orientation | vertical | horizontal | vertical | Layout of the options. Horizontal only for two or three short labels; it wraps rather than overflows. |
| required | boolean | false | An option must be selected to submit. Shown in the legend, not only by color. |
| invalid | boolean | false | Marks the group as failing validation. Usually set by the Form; can be set directly. |
| disabled | boolean | false | Disables every option. Individual options use `options[].disabled`. The group's state folds into each option's, so every radio announces as disabled rather than only the group. A disabled group submits nothing and validates clean — the form value is cleared and no validity is set, as a native disabled control does — so a disabled required group never blocks a submit. |
| description | string | — | Persistent helper text under the legend. Accessibility: Linked to the group with aria-describedby / accessibilityHint. |
| error | string | — | The group's error message. Setting it marks the group invalid. Accessibility: Rendered once under the group with role=alert and linked with aria-describedby on the group. |
Events
1 row| Event | Type | Description |
|---|---|---|
| onChange | onChange / change | Fired when the selection changes, with the new option value. |
Accessibility
Decisions this component leaves to you
Supply the label. It is associated with the control for you, but the words are yours — a placeholder is not a label.
Supply the error text when the value is invalid. The component links and announces it; it cannot write it.
Required props that carry accessibility meaning: label. Each is described in the props table above.
Implicit ARIA role: radiogroup.
Anatomy
What generation guarantees
Label association — The label is programmatically associated with the control, not merely placed beside it.
Arrow navigation — Arrow keys move between items; Home and End jump to the ends.
Roving tabindex — The group is a single tab stop; the arrow keys move within it.
Error identification — An invalid value is identified in text and linked to the control — never signalled by colour alone.
Visible focus — Keyboard focus is always drawn, from the theme’s focus tokens, and is never removed.
Keyboard operable — Everything the pointer can do here, the keyboard can do — see the keyboard section.
Target size — Interactive targets are at least 24×24 CSS pixels (WCAG 2.2 Target Size, minimum).
Contrast — Text and meaningful non-text contrast meets WCAG AA; the pairs are listed below.
This component does not animate, so it has no reduced-motion behaviour to describe.