# RadioGroup

> Slug: `radio` · Category: Form Controls

## When to use

- Selecting exactly one option from a small set (2–7) of mutually exclusive choices.
- Plain text-only options where each option needs at most a short body line.

## When NOT to use

- Rich choices (price, status, leading icon, action) — use `StyledRadioGroup`.
- Many options or known list — use `Select`.
- Multi-select — use `Checkbox` list or `Chips variant="toggle"`.
- Tag-style single-select — consider `Chips variant="radio"`.

## Variants & decision tree

`RadioGroup` is data-driven — pass a `radioButtons` array, NOT compound children. Each option may include an `optionalBody` for a short description.

## States

Per option: unchecked, checked, focused, hover, disabled. Group: error (`errorMessage`), required (`ariaRequired`).

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `legend` | `string` | — | Required. Fieldset legend (group label). |
| `groupName` | `string` | — | Required. Shared `name` for radio inputs. |
| `radioButtons` | `RadioButtonOption[]` | — | Required. Array of options. |
| `value` | `string` | — | Controlled selected `value`. |
| `defaultValue` | `string` | — | Uncontrolled initial selection. |
| `onChange` | `(value, event) => void` | — | Fires with the selected option `value`. |
| `errorMessage` | `string` | — | Group-level error. |
| `ariaRequired` | `boolean` | `false` | Required field marker. |
| `legendAsLabel` | `boolean` | `false` | Render legend as a visible label. |
| `inverse` | `boolean` | `false` | Dark-background styling. |

`RadioButtonOption`: `{ id, value, label, checked?, disabled?, optionalBody? }`.

## Usage examples

```tsx
<RadioGroup
  legend="Plan type"
  groupName="planType"
  value={plan}
  onChange={(value) => setPlan(value)}
  radioButtons={[
    { id: 'home', value: 'home', label: 'Home Internet' },
    { id: 'biz', value: 'biz', label: 'Business Internet' },
  ]}
/>

// With body copy and validation
<RadioGroup
  legend="Shipping speed"
  groupName="shipping"
  ariaRequired
  errorMessage={!ship ? 'Please choose a shipping option' : undefined}
  radioButtons={[
    { id: 'std', value: 'std', label: 'Standard', optionalBody: '5–7 business days, free' },
    { id: 'exp', value: 'exp', label: 'Express', optionalBody: '2 days, $9.99' },
  ]}
/>
```

## Composition

Use within a `<form>`. For richer cards (price, decoration, status), switch to `StyledRadioGroup` with the same prop shape.

## Accessibility

Renders a `<fieldset>` with a `<legend>` — `legend` is required. `groupName` makes radios mutually exclusive. The component wires labels via `htmlFor`/`id`; do not nest `<label>` manually. `errorMessage` is announced via `aria-describedby`.

## Do / Don't

- DO use a clear, descriptive `legend` ("Choose a plan", not "Plans").
- DO pass `value` for controlled groups; React's `onChange` returns the value directly.
- DON'T use compound children — `<RadioGroup><Radio /></RadioGroup>` is not supported.
- DON'T use a radio group for 1 option or for 8+ options.
