# Radio Button

_Box Kite 2.2.5 · a markdown copy of https://www.box-kite.dev/radiobutton/_

One choice out of a set. RadioGroup is the APG pattern — a named group, a shared field name, and the arrow keys between the options.

**Import**

```jsx
import RadioGroup from '@box-kite/react/components/radioGroup';
import RadioButton from '@box-kite/react/components/radioButton';
```

**A group**

```jsx
<RadioGroup label="Plan" name="plan" defaultValue="free">
  <RadioGroup.Item value="free" label="Free" />
  <RadioGroup.Item value="pro" label="Pro" />
  <RadioGroup.Item value="team" label="Team" />
</RadioGroup>
```

## What the group is for

A set of radios with nothing over it is, to a screen reader, a handful of unrelated controls. `RadioGroup` gives them `role="radiogroup"` named by its own label, hands every `RadioGroup.Item` the same `name` — so the set submits as one field, generated if you do not supply one — and owns the selected value.

The tab order it deliberately leaves alone. A native radio set is already a single tab stop, with the checked option holding it; a `tabIndex` of ours would only fight the platform. The arrow keys are the half worth owning, and they select as they move, which is what APG asks of a radio group and what distinguishes it from a listbox.

**Horizontal**

```jsx
<RadioGroup label="Billing" orientation="horizontal" defaultValue="monthly">
  <RadioGroup.Item value="monthly" label="Monthly" />
  <RadioGroup.Item value="yearly" label="Yearly" />
</RadioGroup>
```

**Controlled, with the reason it changed**

```jsx
// A radio group can also hold nothing, so the state is string | undefined.
const [plan, setPlan] = useState<string | undefined>('free');

<RadioGroup
  label="Plan"
  value={plan}
  onValueChange={(next, { reason }) => {
    setPlan(next);
    console.log(reason); // 'click' | 'keyboard'
  }}
>
  <RadioGroup.Item value="free" label="Free" />
  <RadioGroup.Item value="pro" label="Pro" />
</RadioGroup>
```

**One radio on its own**

```jsx
<RadioButton name="plan" value="free" label="Free" defaultChecked />
<RadioButton name="plan" value="pro" label="Pro" />
```

## The label is the component's job

`label` renders the text inside a `<label>` that wraps the input, so the two are associated with no `htmlFor`/`id` pair to keep in sync and the whole row is a click target. Style that element with `labelProps`; every other Box prop still styles the radio itself.

```jsx
<RadioButton name="plan" value="pro" label="Pro" labelProps={{ gap: 3, fontSize: 14 }} />
```

**Disabled**

```jsx
<RadioButton name="plan" value="free" label="Free" disabled defaultChecked />
<RadioButton name="plan" value="pro" label="Pro" disabled />
```

**Clean**

```jsx
<RadioButton clean name="plan" value="free" label="Free" defaultChecked />
```

## RadioButton props

Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on [/box](https://www.box-kite.dev/box.md) rather than repeated here.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `name` | `string` | — | The name the radios of one set share — it is what makes them one choice rather than several. |
| `props` | `RadioButtonTagProps` | — | Attributes for the `<input>` itself. `type` is the component's. |
| `onInput` | `(e: React.ChangeEvent<HTMLInputElement>) => void` | — | Fires on every click or keystroke. On a radio this is the same moment as `onChange`. |
| `onChange` | `(e: React.ChangeEvent<HTMLInputElement>) => void` | — | Fires when this radio becomes the chosen one. A radio never fires it on being cleared. |
| `value` | `string \| number` | — | What the set submits when this option is the chosen one. |
| `autoFocus` | `boolean` | — | Focus it on mount. One per page at most — a stolen focus is a lost place in the document. |
| `readOnly` | `boolean` | — | Rendered and read, but not selectable. Unlike `disabled` it stays in the tab order and submits. |
| `defaultChecked` | `boolean` | — | Makes this the chosen option to begin with, when the set is uncontrolled. |
| `label` | `React.ReactNode` | — | The text beside the radio, rendered inside a `<label>` that wraps the input. |
| `labelProps` | `BoxProps<'label'>` | — | Styles for the wrapping `<label>`: the row's layout, not the radio's own appearance. |

## RadioButton keyboard

| Key | Result |
| --- | --- |
| `Tab` | Enters the set once, landing on the chosen option — or on the first, when none is chosen. Tab again leaves the set entirely. |
| `Down / Up, Right / Left` | The next or previous radio of the same `name`, choosing it as focus arrives, wrapping at both ends. The browser supplies this, so it works with no `RadioGroup`. |
| `Space` | Chooses the focused radio. |

## RadioButton accessibility

- A real `<input type="radio">`, so the checked state, the roving tab stop across a `name` and form submission are the platform's rather than this component's.
- `label` renders the `<label>` wrapping the input, which is what names this option. What names the *set* is a `RadioGroup`, or a `<fieldset>` with a legend of your own.

Swept with axe on every release, in this state: `RadioButton`. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.

## RadioButton style tree

Every part the component draws is a node with a name, so a default can be restyled with `Box.components()` instead of a selector — and a variant is a name too.

`radioButton`

## RadioGroup props

Everything below is this component’s own. All 235 of Box’s style props work on it too, and those are on [/box](https://www.box-kite.dev/box.md) rather than repeated here.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `label` | `React.ReactNode` | — | The group's name, rendered above it and used as the group's accessible name. |
| `name` | `string` | — | The `name` every radio in the group submits under. Generated when left out. |
| `value` | `string` | — | Controlled selection. Leave it out and the group owns it. |
| `defaultValue` | `string` | — | The option chosen to begin with, when the group owns its value. |
| `onValueChange` | `ChangeHandler<string \| undefined, RadioGroupReason>` | — | Fires with the new value and why it changed — `'click'` or `'keyboard'`. A group can also hold nothing, so the value is `string \| undefined`. |
| `onChange` | `ChangeHandler<string \| undefined, RadioGroupReason>` | — | The same callback under its old name. Both fire, so either one can be passed. |
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Which way the radios stack. Both arrow pairs navigate either way, as APG specifies. |
| `children` | `React.ReactNode` | — | The options: `RadioGroup.Item` elements, or anything else the group should hold. |

### RadioGroup.Item

A radio that reads its group: the shared `name`, whether it is selected, and what to report when it is chosen. Outside a `RadioGroup` it is a plain `RadioButton`.

| Prop | Type | Default | What it does |
| --- | --- | --- | --- |
| `value`required | `string` | — | What this radio submits, and what the group reports when it is chosen. |
| `name` | `string` | — | Only for an item rendered outside a group — inside one, the group supplies the name. |
| `label` | `React.ReactNode` | — | The text beside the radio, rendered inside a label that wraps the input. |
| `labelProps` | `BoxProps<'label'>` | — | Styles for the wrapping `<label>`: the row's layout, not the radio's own appearance. |
| `props` | `BoxTagProps<'input'>` | — | Attributes for the `<input>` itself. `type`, `name` and `checked` are the group's. |
| `ref` | `Ref<HTMLInputElement>` | — | The `<input>` element, for a caller that has to focus or measure this option. |

## RadioGroup keyboard

| Key | Result |
| --- | --- |
| `Tab` | Enters the group once, landing on the chosen option — or on the first, when none is chosen. Tab again leaves the group entirely. |
| `Down / Right` | The next option, choosing it as focus arrives, wrapping to the first. |
| `Up / Left` | The previous option, the same way, wrapping to the last. |
| `Space` | Chooses the focused option. The platform supplies this one. |

## RadioGroup accessibility

Implements [the WAI-ARIA APG pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).

- `role="radiogroup"`, named by its own `label` through `aria-labelledby` — radios with nothing over them are unrelated controls to a screen reader.
- `aria-orientation` follows `orientation`, and every item shares one generated `name`, so the set submits as one field.
- The tab order is deliberately the platform's: a native radio set is already one tab stop with a roving tabindex, which a `tabIndex` of ours would fight.

Swept with axe on every release, in this state: `RadioGroup`. No violations, with contrast and landmark rules left to a human. Screen-reader results are not published yet.

## RadioGroup style tree

Every part the component draws is a node with a name, so a default can be restyled with `Box.components()` instead of a selector — and a variant is a name too.

`radioGroup`variants: horizontal

`radioGroup.label`

---

_Every page: https://www.box-kite.dev/llms.txt · every prop, measured: https://www.box-kite.dev/props.md_

