---
outline: deep
---

# Radio group

A set of native radios, shown as the classic dot or as joined buttons — the form counterpart of a [segmented control](./segmented-control.md).

**`.l-radio-group`** — Native HTML Element

```html
<fieldset class="l-radio-group">
  <legend>View</legend>
  <label>
    <input
      type="radio"
      class="l-radio"
      name="view"
      value="list"
      checked
    />
    List
  </label>
  <label>
    <input
      type="radio"
      class="l-radio"
      name="view"
      value="board"
    />
    Board
  </label>
</fieldset>
```

The group is a `<fieldset>` captioned by its `<legend>`, and each item is a `<label>` wrapping one `<input type="radio">`. That is the platform's own way to group and name a set of radios, so there is no ARIA to add and no `for`/`id` pair to keep in sync. There is **no JavaScript** either: radios sharing a `name` already give single selection, arrow-key navigation, form submission, reset and validation — this is a stylesheet, not a component.

That is the difference with [segmented control](./segmented-control.md): use this when the value is submitted with a form, and the segmented control when the choice takes effect immediately.

## Options

### Appearance

#### Default

The radio primitive: put `.l-radio` on each input.

```html
<fieldset
  class="l-radio-group"
  data-orientation="vertical"
>
  <legend>Notifications</legend>
  <label>
    <input
      type="radio"
      class="l-radio"
      name="notifications"
      value="all"
      checked
    />
    All new messages
  </label>
  <label>
    <input
      type="radio"
      class="l-radio"
      name="notifications"
      value="mentions"
    />
    Direct messages and mentions
  </label>
  <label>
    <input
      type="radio"
      class="l-radio"
      name="notifications"
      value="none"
    />
    Nothing
  </label>
</fieldset>
```

#### Button

Add `data-appearance="button"` on the group and `.l-button` on each label. The items are joined into a single unit and the selected one keeps the button's active fill.

```html
<fieldset
  class="l-radio-group"
  data-appearance="button"
>
  <legend>View</legend>
  <label class="l-button">
    <input
      type="radio"
      name="view"
      value="list"
      checked
    />
    List
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="view"
      value="board"
    />
    Board
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="view"
      value="calendar"
    />
    Calendar
  </label>
</fieldset>
```

### Naming the group

The `<legend>` names the group — the technique [WCAG H71](https://www.w3.org/WAI/WCAG22/Techniques/html/H71) and [RGAA 11.6](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.6) ask for. When no visible caption fits, a view switcher in a toolbar for instance, drop the `<legend>` and name the `<fieldset>` with `aria-label` instead.

```html
<fieldset
  class="l-radio-group"
  data-appearance="button"
  aria-label="View"
>
  <label class="l-button">…</label>
</fieldset>
```

### Orientation

Add `data-orientation="vertical"` to stack the items. Works in both appearances.

```html
<fieldset
  class="l-radio-group"
  data-appearance="button"
  data-orientation="vertical"
>
  <legend>Delivery</legend>
  <label class="l-button">
    <input
      type="radio"
      name="delivery"
      value="standard"
      checked
    />
    Standard
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="delivery"
      value="express"
    />
    Express
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="delivery"
      value="pickup"
    />
    Pickup
  </label>
</fieldset>
```

### Sizes

In the button appearance, set `data-size` on every label so the group stays even. Any `.l-button` size works.

```html
<fieldset
  class="l-radio-group"
  data-appearance="button"
>
  <legend>Small</legend>
  <label
    class="l-button"
    data-size="sm"
  >
    <input
      type="radio"
      name="density-sm"
      value="compact"
      checked
    />
    Compact
  </label>
  <label
    class="l-button"
    data-size="sm"
  >
    <input
      type="radio"
      name="density-sm"
      value="cosy"
    />
    Cosy
  </label>
</fieldset>

<fieldset
  class="l-radio-group"
  data-appearance="button"
>
  <legend>Medium</legend>
  <label class="l-button">
    <input
      type="radio"
      name="density-md"
      value="compact"
      checked
    />
    Compact
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="density-md"
      value="cosy"
    />
    Cosy
  </label>
</fieldset>

<fieldset
  class="l-radio-group"
  data-appearance="button"
>
  <legend>Large</legend>
  <label
    class="l-button"
    data-size="lg"
  >
    <input
      type="radio"
      name="density-lg"
      value="compact"
      checked
    />
    Compact
  </label>
  <label
    class="l-button"
    data-size="lg"
  >
    <input
      type="radio"
      name="density-lg"
      value="cosy"
    />
    Cosy
  </label>
</fieldset>
```

### Disabled segment

Add `disabled` to a segment's input. Arrow keys skip it, and it drops out of the tab order — both from the browser, not from us.

```html
<fieldset
  class="l-radio-group"
  data-appearance="button"
>
  <legend>Plan</legend>
  <label class="l-button">
    <input
      type="radio"
      name="plan"
      value="free"
      checked
    />
    Free
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="plan"
      value="pro"
    />
    Pro
  </label>
  <label class="l-button">
    <input
      type="radio"
      name="plan"
      value="enterprise"
      disabled
    />
    Enterprise
  </label>
</fieldset>
```

## Accessibility

### Criteria

- **Role** — Native `<input type="radio">` controls expose `radio` and their checked state without any ARIA
- **Group name** — The `<fieldset>` groups the radios and its `<legend>` names them, natively. Without a visible caption, `aria-label` on the fieldset
- **Segment name** — The `<label>` wraps its input, so its text is the accessible name — no `for`/`id` pair needed
- **Keyboard** — Arrow keys move and select through the group; the group is a single `Tab` stop. Browser behaviour, not scripted
- **Focus ring** — In the button appearance the input is transparent but focusable, so the ring is mirrored onto its label and raised above the joined borders
- **Target size** — In the button appearance a segment is a full `.l-button`, so it clears the 24px minimum at every size

### Rules

- The group is a `<fieldset>`, named by a `<legend>` first child — or by `aria-label` when no visible caption fits
- Every input in the group shares one `name`
- Wrap each input in a `<label>` — that names it and makes it the click target, with no `for`/`id` pair
- For the button appearance, set `data-appearance="button"` on the group and `.l-button` on every label; otherwise put `.l-radio` on every input
- Never hide the input with `display: none` or `visibility: hidden`: it would leave the accessibility tree and the tab order

### Keyboard interactions

- `Tab` — Moves focus into the group, onto the checked segment
- `ArrowRight` — Selects and focuses the next segment, wrapping
- `ArrowLeft` — Selects and focuses the previous segment, wrapping
- `ArrowDown` — Same as ArrowRight
- `ArrowUp` — Same as ArrowLeft
- `Space` — Selects the focused segment when none was selected

## API reference

### Importing

```css
@import 'luxen-ui/css/radio-group';
```

The segments are `.l-button` elements, so `luxen-ui/css/button` is required too.

### Attributes & Properties

- **aria-label** — Names the group when no visible `<legend>` fits — a view switcher in a toolbar, say. Prefer a `<legend>`: it is the technique WCAG H71 and RGAA 11.6 ask for.
- **data-appearance** — button — Renders the items as joined buttons: put `.l-button` on each label. Omit it for the radio primitive: put `.l-radio` on each input.
- **data-orientation**: `horizontal | vertical` — Stacks the items. Defaults to horizontal.

### CSS classes

- `.l-radio-group` — Applied to the `<fieldset>` grouping the radios. Its first child is the `<legend>` naming them; each item is a `<label>` wrapping one `<input type="radio">`.
