---
outline: deep
---

# Radio

Radios let users pick a single option from a set of mutually exclusive choices.

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

```html
<fieldset>
  <legend>Plan</legend>
  <l-form-field>
    <label>Free</label>
    <input
      type="radio"
      name="plan"
      value="free"
      checked
    />
  </l-form-field>
  <l-form-field>
    <label>Pro</label>
    <input
      type="radio"
      name="plan"
      value="pro"
    />
  </l-form-field>
</fieldset>
```

[`l-form-field`](/elements/form-field) auto-styles a bare `<input type="radio">` and wires the accessibility (label, hint, error, `aria-*`); standalone, apply `.l-radio` to the input yourself. Radios that share a `name` form a group — selecting one deselects the others, and arrow keys move between them. Wrap the group in a `<fieldset>` with a `<legend>` for the shared question.

For two opposite options where neither is a sensible default, prefer a single checkbox or a switch. For more than ~5 options, prefer a [select](/elements/select).

## Options

### Selected

Native `checked` attribute. Only one radio per `name` group can be selected.

### Disabled

Native `disabled` attribute.

### States

```html
<div class="flex flex-col gap-3">
  <l-form-field>
    <label>Unselected</label>
    <input
      type="radio"
      name="states"
    />
  </l-form-field>
  <l-form-field>
    <label>Selected</label>
    <input
      type="radio"
      name="states"
      checked
    />
  </l-form-field>
  <l-form-field>
    <label>Disabled</label>
    <input
      type="radio"
      name="states-disabled"
      disabled
    />
  </l-form-field>
  <l-form-field>
    <label>Disabled selected</label>
    <input
      type="radio"
      name="states-disabled-selected"
      checked
      disabled
    />
  </l-form-field>
  <l-form-field invalid>
    <label>Invalid (required, none selected)</label>
    <input
      type="radio"
      name="states-invalid"
      required
    />
  </l-form-field>
</div>
```

### Invalid

Styled via `:user-invalid` (after interaction) or by setting `aria-invalid="true"`. Inside `l-form-field` this is managed for you.

### Size & accent

Override `--size` for the box and `--accent` for the selected fill.

```html
<div class="flex flex-col gap-3">
  <l-form-field>
    <label>Default</label>
    <input
      type="radio"
      name="sizing-1"
      checked
    />
  </l-form-field>
  <l-form-field>
    <label>Large</label>
    <input
      type="radio"
      class="[--size:1.5rem]"
      name="sizing-2"
      checked
    />
  </l-form-field>
  <l-form-field>
    <label>Extra large, dark accent</label>
    <input
      type="radio"
      class="[--size:2rem] [--accent:var(--l-color-gray-900)]"
      name="sizing-3"
      checked
    />
  </l-form-field>
</div>
```

## Examples

### Group

Wrap the group in a `<fieldset>` with a `<legend>` so assistive tech announces the shared question.

```html
<fieldset class="m-0 border-0 p-0">
  <legend class="mb-3 text-sm font-medium">Notifications</legend>
  <div class="flex flex-col gap-3">
    <l-form-field>
      <label>All new messages</label>
      <input
        type="radio"
        name="notifications"
        value="all"
        checked
      />
    </l-form-field>
    <l-form-field>
      <label>Direct messages and mentions</label>
      <input
        type="radio"
        name="notifications"
        value="mentions"
      />
    </l-form-field>
    <l-form-field>
      <label>Nothing</label>
      <input
        type="radio"
        name="notifications"
        value="none"
      />
    </l-form-field>
  </div>
</fieldset>
```

### With a hint

`l-form-field` wires the accessibility (label, hint, `aria-*`). Each option is its own field; group them inside a `<fieldset>`.

```html
<fieldset class="flex flex-col gap-3 border-0 p-0 m-0">
  <legend class="mb-2 font-medium">Plan</legend>
  <l-form-field>
    <label>Free</label>
    <input
      type="radio"
      name="plan"
      value="free"
      checked
    />
    <p class="l-hint">For individuals getting started.</p>
  </l-form-field>
  <l-form-field>
    <label>Pro</label>
    <input
      type="radio"
      name="plan"
      value="pro"
    />
    <p class="l-hint">For growing teams that need more.</p>
  </l-form-field>
</fieldset>
```

## Accessibility

### Criteria

- **Role** — Uses native `<input type=radio>` — built-in `radio` semantics, grouped into a `radiogroup` by a shared `name` inside a `fieldset`
- **Group name** — Wrap the group in `<fieldset>` with a `<legend>` so the shared question is announced
- **Accessible name** — Each radio must have an associated `<label>` (wrap the input or use `for`/`id`)
- **Target size** — The label is part of the click target; keep the interactive area at least 24×24px
- **Focus visible** — Keyboard focus shows a 2px outline via `:focus-visible`
- **Required state** — Native `required` on the group communicates a mandatory choice to assistive tech

### Rules

- Always pair each radio with a `<label>` — wrap the input or link with `for`/`id`
- Group related radios with a shared `name` and wrap them in `<fieldset>` + `<legend>`
- Pre-select a sensible default unless the choice must be deliberate
- For a single on/off setting, use a checkbox or a switch — not one radio

### Keyboard interactions

- `Tab` — Moves focus into the group (to the selected radio, or the first if none is selected)
- `Arrow keys` — Moves between radios in the group and selects the focused one
- `Space` — Selects the focused radio

## API reference

### Importing

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

### Attributes & Properties

- **name** — Groups radios so only one can be selected at a time. Every radio in a group shares the same `name`.
- **value** — The value submitted with the form when this radio is the selected one in its group.
- **checked** — Whether the radio is selected.
- **disabled** — Disables the radio.
- **required** — Marks the group as required for form submission.
- **aria-invalid** — Set to `true` to force the invalid style (otherwise applied via `:user-invalid`). `l-form-field` manages this automatically.

### CSS classes

- `.l-radio` — Base radio style, applied to `<input type="radio">`. Inside `l-form-field` a bare radio is auto-styled, so the class is optional there.

### CSS custom properties

- `--size` (default: `1.25em`) — Box size.
- `--accent` (default: `var(--l-form-control-activated-color)`) — Selected fill color.
- `--dot` — Selected dot icon as a `url()`. Override to swap the SVG (color is baked into the image).
