Skip to the content

Kongens Nytorv

Metro M1, M2 and M3.

Theme
Match the systemLightDark

Variants and states

RestDisabled
Default
Theme
Match the systemLightDark
Theme
Match the systemLightDark
Default
  • Rest
    Theme
    Match the systemLightDark
  • Disabled
    Theme
    Match the systemLightDark

RadioGroup styles hover, pressed and focus on its inner parts, which a still preview can't reach, so this table shows rest and disabled only.

Props

NameTypeDefaultDescription
label*ReactNode—Required. The group's name, in sentence case, a word or a short question, such as "Theme": shown above the radios, and the group's accessible name.
hiddenLabelbooleanfalseWhether the label is heard but not seen. Use it where a heading right above already names the group for the eye, so the group keeps its name for the ear.
valuestring—The checked radio's value, when the caller controls it. A value no radio has checks none.
defaultValuestring—The radio checked at first, when the group keeps the value itself. Left out, with no value, no radio is checked until one is chosen.
onChange(event: Event, value: string, details: RadioGroup.ChangeEventDetails) => void—Called when another radio is checked, by a click, an arrow key or Space, with that radio's value; details.reason is 'none', and details.cancel() keeps an uncontrolled group as it was. Not called when the checked radio is clicked again. event is details.event.
namestring—The name the checked radio's value is submitted under in a native <form>. Inside a Base UI Field.Root, the field's name wins.
disabledbooleanfalseWhether every radio ignores the pointer and the keys. The checked radio still shows, and the group keeps one tab stop, on the checked radio or the first, announced as dimmed, that changes nothing.
readOnlybooleanfalseWhether the group shows its choice but can't be changed. It keeps its tab stop, the arrow keys move focus without checking, and it is announced as read-only.
requiredbooleanfalseWhether a radio must be checked before its form submits. Announced as required; the label says so to the eye.
childrenReactNode—The group's Radios, in reading order, below the label.
classNamestring—Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
styleCSSProperties—Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
refRef<HTMLDivElement>—Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
slotProps{ legend?: FieldsetLegendProps | undefined; }—Props for each part: legend.
label (required)ReactNode, default —
Required. The group's name, in sentence case, a word or a short question, such as "Theme": shown above the radios, and the group's accessible name.
hiddenLabelboolean, default false
Whether the label is heard but not seen. Use it where a heading right above already names the group for the eye, so the group keeps its name for the ear.
valuestring, default —
The checked radio's value, when the caller controls it. A value no radio has checks none.
defaultValuestring, default —
The radio checked at first, when the group keeps the value itself. Left out, with no value, no radio is checked until one is chosen.
onChange(event: Event, value: string, details: RadioGroup.ChangeEventDetails) => void, default —
Called when another radio is checked, by a click, an arrow key or Space, with that radio's value; details.reason is 'none', and details.cancel() keeps an uncontrolled group as it was. Not called when the checked radio is clicked again. event is details.event.
namestring, default —
The name the checked radio's value is submitted under in a native <form>. Inside a Base UI Field.Root, the field's name wins.
disabledboolean, default false
Whether every radio ignores the pointer and the keys. The checked radio still shows, and the group keeps one tab stop, on the checked radio or the first, announced as dimmed, that changes nothing.
readOnlyboolean, default false
Whether the group shows its choice but can't be changed. It keeps its tab stop, the arrow keys move focus without checking, and it is announced as read-only.
requiredboolean, default false
Whether a radio must be checked before its form submits. Announced as required; the label says so to the eye.
childrenReactNode, default —
The group's Radios, in reading order, below the label.
classNamestring, default —
Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
styleCSSProperties, default —
Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
refRef<HTMLDivElement>, default —
Reaches the root, the <div role="radiogroup">. className is merged after pn-RadioGroup and Fieldset's pn-Fieldset.
slotProps{ legend?: FieldsetLegendProps | undefined; }, default —
Props for each part: legend.

Use it

import { RadioGroup } from '@mylder/perron'

Ask your agent

get_reference({ library: "base-ui", component: "RadioGroup" })

Keyboard and ARIA

From the Base UI reference for RadioGroup and Radio and the installed RadioGroup, RadioRoot, the composite root they build on and useButton. "From" says who does it.

KeyDoesFrom
TabMoves focus into the group, to its one tab stop: the checked radio, or the first enabled one while none is checked; the next Tab leaves the group. A disabled group, or a checked radio disabled alone, keeps that stop, announced as dimmed, where Space changes nothing; from a checked radio disabled alone the arrow keys move on to the enabled radiosBase UI
Shift+TabMoves focus out of the group, backwardsBase UI
ArrowDown, ArrowRightMoves focus to the next enabled radio and checks it, calling onChange; wraps from the last to the first. Under an rtl DirectionProvider, which Perron doesn't set up, ArrowRight goes back. Read-only: moves focus onlyBase UI
ArrowUp, ArrowLeftMoves focus to the previous enabled radio and checks it, calling onChange; wraps from the first to the last. Read-only: moves focus onlyBase UI
SpaceChecks the focused radio, calling onChange, if it isn't checked already; nothing while read-only or disabledBase UI, through the hidden input
EnterNothing: the installed Radio.Root stops it, as a native radio doesBase UI

Home and End do nothing (the installed group turns enableHomeAndEndKeys off), and the component adds no keys of its own.

ElementRoleAccessible name fromStates announced
rootradiogroup, from Base UI, on a <div>aria-labelledby, the legend's id, from Base UI through Fieldset: the label's text, seen or hiddenaria-disabled, aria-readonly, aria-required; in an invalid Field, aria-invalid, with the Field's description and error as its description through aria-describedby
legendnone: a <div>, or a <span> when hiddennone: its text names the rootnone
radioradio, from Base UI, on a <span>Its content: the label's text, the caller's children; the indicator holds no textaria-checked; aria-disabled; aria-invalid in an invalid Field; its place in the group, "2 of 3"
indicatornone: a <span> with no contentnonenone
inputnone: aria-hidden="true", tabIndex −1nonenone

What axe won't catch, from the contract, each with how this component meets it:

  • Everything the eye is told, the ear is told, for every shape each prop accepts: the checked radio's filled circle and dot are aria-checked="true" and every other radio's "false"; a disabled radio's tertiary colours are aria-disabled, heard as dimmed, on each radio of a disabled group; a read-only group is aria-readonly; an invalid radio's danger edge is aria-invalid, with the Field's message as its description; the label the eye reads above the radios is the group's name, and a hidden label still is.
  • Every landmark and named region has an accessible name: the root is a radiogroup, not a landmark, and is always named by its label, which is required (CR-2).
  • Every default label can be overridden: none here; the component owns no strings.
  • Every shape the type accepts renders, in every variant: label and a radio's children render a string, an element and a list alike: Default names its group with a string, and LabelShapes with a list and, in its play test, an element, each heard as one run, over radios whose words are a string, an element and a list; a radio's words sit in one span, so text and elements flow and wrap as one run beside the mark.
  • A hidden copy kept for animation is aria-hidden, and a collapsed region is inert: the hidden inputs are Base UI's, aria-hidden and out of the tab order; the indicator kept mounted while unchecked holds no text, so it adds nothing to the tree; nothing collapses.
  • Native semantics first: Base UI's radiogroup and radio roles with aria-checked, with a native <input type="radio"> beside each for the form; with render and nativeButton, a native <button>. Perron adds no ARIA.
  • Focus moves in reading order and returns on close: the arrow keys move through the radios in document order, below their label, and Tab leaves the group after its one stop; focus stays on the radio it checks; the group opens nothing to return from.
  • Every target is at least size.target-min square: the smallest target is a radio with a one-letter label, size.target-min (24 px) tall and the 16 px circle, the space.200 gap and the letter (at least 24 px) wide.
  • Loops stop when the system asks for less motion: no loop, and no transition; a radio's state changes at once.

Owned strings

No owned strings: every word RadioGroup shows or announces comes from the caller, through label and each radio's children. Base UI's roles say checked or not checked, and a radio's place in its group, in every locale with no word of Perron's.

Do and don't

For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.

RuleLevelDoDon'tChecked by
DD-1shouldUse a RadioGroup for one answer of two to six that a person reads in full before choosing, such as the theme or the languageUse it for more choices than fit a panel, or for choices that need a search: a select or a search field keeps a long list out of the wayreview
DD-2shouldSwitch how one view shows its content with a SegmentedControl, and filter the map with a FilterChipUse a row of radios for a view's mode or the top bar's filters: a radio is a form answer, read down a columnreview
DD-3mustName the group with label, in sentence case, a word or a short question: "Theme"Leave out the label, or name the group with aria-label where there is room to show one: a sighted person can't tell what an unnamed set of choices answersreview
DD-4mustWrite each radio's words as a short noun phrase in sentence case, of a similar length and shape: "Match the system", "Light", "Dark"Put a link or a button inside a radio's words: a radio's content is presentational, so a screen reader never reaches it, and a click on it checks the radio tooreview
DD-5shouldCheck the safest or most common choice with defaultValue, or leave all unchecked with required when the person must decideCheck a choice the person never made and then treat it as their answer: a pre-checked radio is easy to pass byreview
DD-6shouldDisable the whole group with disabled, or one radio that can't be chosen now, and say why in the label or the Field's descriptionDisable a radio with no word of why, or hide it: a dimmed choice can't explain itself, and a missing one moves the othersreview
DD-7mustGive a hint or an error through Base UI's Field.Root round the group, with Field.Description and Field.Error and no Field.LabelPut a hint or an error as plain text among the radios: nothing ties it to the group for the ear, and the arrow keys never reach itreview
DD-8mustName the group with its own labelWrap the group in Perron's Field: its label renames every radio in it, so each radio is heard as the field's label, never its own wordsreview
DD-9shouldUse a checkbox for a choice that can be on or off on its own, or several at onceUse a RadioGroup of one radio, or of "Yes" and "No" for a setting that takes effect at once: one radio can't be unchecked, and a setting that applies now is a switchreview