Skip to main content

Radio Button

Allows selection of exactly one option from a set of mutually exclusive choices. Use when options are few enough to display simultaneously and the user must choose only one. Don't use for binary on/off settings.

OverviewStyleAccessibility

Description

A lone radio button is meaningless. Radio buttons only make sense as a group of two or more mutually exclusive options, always wrapped in a <fieldset>.

Anatomy

PartRequired?Notes
Radio control Required The circular input. Must meet minimum 44×44px touch target size.
Selected indicator Conditional A filled inner circle shown when the option is selected.
Label Required Describes the option. Always visible and associated via <label for> or wrapping.
Group container Required A <fieldset> wrapping the entire group. Always required — a radio button group without a fieldset is inaccessible.
Group label Required A <legend> that describes the question or category the group answers. Read by screen readers before each option.

States

State Behaviour
Unselected Default. Empty circle.
Selected Circle with filled inner dot. Only one per group.
Disabled Greyed out. If the whole group is disabled, disable all inputs and communicate why.
Focus 2px focus ring on the radio control, --op-color-focus-ring.
Error Red border on controls + error message below group when no selection is made for a required field.

Usage guidelines

Radio Button vs Checkbox vs Dropdown

Control Use when
Radio button One option from a small set (2–6). All options should be visible without interaction. Selection is mutually exclusive.
Checkbox One or more options can be selected simultaneously.
Dropdown One option from a larger set (7+), or when space is constrained. Options are hidden until the user opens the dropdown.
Switch A single on/off setting that takes effect immediately.

Do / Don't

Do

Pre-select a default when one option is recommended or most common. An empty radio group forces the user to make a decision they may not have needed to think about.

Don't

Don't use radio buttons for more than 6–7 options. Switch to a dropdown when the list grows long enough that comparison becomes difficult.

Do

Order options logically: alphabetically, by frequency of use, or from least to most restrictive (for permissions). Never randomise order.

Don't

Don't allow radio buttons to be deselected by clicking them again. Once selected, a radio should only change via selecting another option.

Layout & Spacing

Element Spec
Control size 12 / 14 / 16 / 20 / 24px (XS to XL), medium 16px
Inner dot size 8 / 8 / 8 / 12 / 16px. --orbit-selection-control-size-*-dot
Hit area 44×44px minimum
Gap (control to label) 4 / 8 / 8 / 10 / 12px by size
Gap (between options) 8 / 10 / 12 / 16 / 24px by size. --orbit-radio-group-size-*-gap
Label font size Same as the control size
Size Control Label Gap
Extra small 12px 12px 4px
Small 14px 14px 8px
Medium (default) 16px 16px 8px
Large 20px 20px 10px
Extra large 24px 24px 12px

The control is the same size as its label text. Values come from --orbit-selection-control-size-{xs|s|m|l|xl}-{control|font-size|gap}; a 44×44px hit area is still required around the smaller sizes.

Component tokens

Checkbox, radio and switch share the --orbit-selection-control-* tokens, so they are framework-neutral CSS variables that any framework (including React Aria components) can use without Web Awesome. Colours follow Light and Dark mode and the product theme, and every token aliases a Semantic token.

Token What it sets
--orbit-selection-control-background, -border Unchecked fill and outline. The border meets 3:1 against the surface.
--orbit-selection-control-checked-background, -checked-border, -checked-icon Checked fill, outline and the check mark or dot.
--orbit-selection-control-state-hover Translucent overlay for the hover state.
--orbit-selection-control-invalid-border, -invalid-message Outline and message colour when invalid.
--orbit-selection-control-disabled-background, -border, -checked-background, -checked-icon, -text Disabled colours.
--orbit-selection-control-label-color, -label-font-weight, -hint-color, -hint-font-size Label and hint text.
`--orbit-selection-control-focus-ring-{color width
--orbit-selection-control-radius, -border-width Corner radius and outline width.

States are Default, Hover, Invalid and Disabled. Focus is shown with the focus ring.

Radio button appearance

The button appearance draws each option as a bordered, button-like segment. A group of them is one connected control: no gap, shared single borders, and rounded corners only on the outer ends (--orbit-radio-group-radius). Heights match Button and the form fields (24 / 32 / 40 / 48 / 56px) through --orbit-radio-button-size-*-height and -padding-inline. Selected segments use --orbit-radio-button-selected-background and -selected-border.

State Border Fill
Unselected --op-color-border-strong --op-color-bg-primary
Selected --op-color-interactive-default --op-color-interactive-default (inner dot)
Disabled --op-color-border-default --op-color-bg-disabled
Error --op-color-status-error --op-color-bg-primary

Engineering notes

  • All radio buttons in a group must share the same name attribute. This is what makes them mutually exclusive — the browser enforces single-selection within a name group.
  • Each radio must have a unique value. Submitting a form with radio buttons requires the value to identify which option was chosen.
  • Don't programmatically deselect a radio by clearing checked without selecting another — users have no way to "uncheck" a radio. If "none" is a valid choice, add it as an explicit option.

Keyboard interaction

KeyAction
Tab Moves focus to the selected radio in the group. If none are selected, focuses the first.
Arrow Up / Arrow Left Moves selection to the previous option in the group.
Arrow Down / Arrow Right Moves selection to the next option in the group.
Space Selects the focused radio button.

Why it matters

Radio groups have a specific keyboard pattern that surprises many developers: the entire group is a single Tab stop, and arrow keys move between options. This is intentional — it keeps forms efficient for keyboard users. Implementing radio buttons as individual Tab stops (common in custom implementations) breaks this contract and forces keyboard users to Tab through every option instead of navigating quickly with arrows.

Required ARIA and HTML

Pattern Requirement
Group wrapping Always use <fieldset> + <legend>. The legend is announced before each option, giving context. Without it, screen reader users hear "Daily — radio button" instead of "Notification frequency — Daily — radio button".
Required field Mark the legend with a required indicator. Connect validation errors via aria-describedby on the fieldset.
Disabled state Use the disabled HTML attribute. Don't use aria-disabled without also suppressing pointer/keyboard interaction.

Things to avoid

  • Don't use tabindex to make individual radio buttons Tab-focusable. The arrow key navigation pattern requires the group to behave as a single Tab stop.
  • Don't substitute a role="radiogroup" + role="radio" ARIA pattern when native <input type="radio"> is available. The native element provides the correct keyboard behaviour with no extra scripting.
  • Don't visually group radios without a programmatic group container. Visual proximity is not enough — the fieldset is what communicates group membership to assistive technology.

Was this page helpful?

Updated 2 October 2026