---
title: Radio
description: Radio button with label and hint
visibility: guest
draft: false
---

# Radio

A form control for single-select lists. Multiple radios sharing the same `name` form a mutually exclusive group.

## Basic Usage

:::preview{title="Radio group"}
<x-wirekit::radio name="plan" value="free" label="Free" checked />
<x-wirekit::radio name="plan" value="pro" label="Pro — €9/month" />
<x-wirekit::radio name="plan" value="team" label="Team — €29/month" />
:::

## With Hint

:::preview{title="Radios with helper text per option"}
<x-wirekit::radio name="billing" value="monthly" label="Monthly" hint="€9 billed each month" />
<x-wirekit::radio name="billing" value="yearly" label="Yearly" hint="€90 per year — save 2 months" />
:::

## Card Variant

`variant="card"` turns each option into a bordered, fully-clickable card that highlights when selected — perfect for pricing tiers and plan pickers. The card reacts to its own input via CSS `:has()`, no JavaScript needed.

:::preview{title="Pricing-tier cards"}
<x-wirekit::stack gap="sm">
    <x-wirekit::radio name="tier" value="starter" variant="card" label="Starter — €9/month, 1 project" checked />
    <x-wirekit::radio name="tier" value="pro" variant="card" label="Pro — €29/month, unlimited projects" />
    <x-wirekit::radio name="tier" value="team" variant="card" label="Team — €99/month, with SSO" />
</x-wirekit::stack>
:::

## Sizes

`sm`, `md` (default), and `lg` scale the circle and its inner dot together, matching the `size` prop on [Checkbox](/components/checkbox) and [Toggle](/components/toggle).

:::preview{title="Radio sizes"}
<x-wirekit::stack gap="sm">
    <x-wirekit::radio name="rs" value="sm" size="sm" label="Small" checked />
    <x-wirekit::radio name="rs" value="md" size="md" label="Medium (default)" />
    <x-wirekit::radio name="rs" value="lg" size="lg" label="Large" />
</x-wirekit::stack>
:::

## Disabled

:::preview{title="Disabled options"}
<x-wirekit::radio name="shipping" value="standard" label="Standard" checked />
<x-wirekit::radio name="shipping" value="express" label="Express (unavailable)" disabled />
:::

## With Error

:::preview{title="Radio group with a group-level error message"}
<fieldset>
    <legend style="margin-bottom: 0.5rem; color: var(--color-wk-text); font-weight: 500;">Choose a plan:</legend>
    <x-wirekit::stack gap="xs">
        <x-wirekit::radio name="plan-error-demo" value="free" label="Free" />
        <x-wirekit::radio name="plan-error-demo" value="pro" label="Pro" error="Please select a plan." />
    </x-wirekit::stack>
</fieldset>
:::

When validation fails, apply the `error` prop to one of the radios (usually the last) so the message appears under the group. Wrap the group in a native `<fieldset>` + `<legend>` for the WCAG-standard group-label semantics — screen readers announce the legend before each radio in the set.

## Livewire Integration

Bind every radio in the group to the same Livewire property via `wire:model.live`. The radios share a `name` so the browser already groups them; `wire:model` synchronizes the chosen value back to the component.

```blade
<x-wirekit::radio name="plan" value="free" label="Free" wire:model.live="selectedPlan" />
<x-wirekit::radio name="plan" value="pro"  label="Pro"  wire:model.live="selectedPlan" />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | string\|null | `null` | Text next to the radio |
| `hint` | string\|null | `null` | Helper text below |
| `error` | string\|null | `null` | Error message (also triggers `aria-invalid`) |
| `value` | string\|null | `null` | Value submitted when selected (required for grouping) |
| `size` | string | `'md'` | Circle size — `sm`, `md`, or `lg` |
| `variant` | string | `'default'` | `default` (inline) or `card` (the whole bordered card is the selectable target) |
| `scope` | string\|null | `null` | Scoped personalization name |

The `id` is auto-generated as `{name}-{value}` when both are given, ensuring unique IDs within a radio group.

## Accessibility

- Native `<input type="radio">` — browser provides full keyboard support:
  - **Arrow keys** navigate between options within the same `name` group
  - **Space** selects the focused radio
  - **Tab** moves focus to the first/checked radio of the next group
- The visual circle and inner dot are decorative (`aria-hidden="true"`).
- For a screen-reader-announced group label, wrap related radios in a native `<fieldset>` with a `<legend>` — this is the WCAG-recommended pattern for grouped form controls and is announced before each radio in the set:

```blade
<fieldset>
    <legend>Choose a plan</legend>
    <x-wirekit::radio name="plan" value="free" label="Free" />
    <x-wirekit::radio name="plan" value="pro" label="Pro" />
</fieldset>
```

If `<fieldset>`'s default border or spacing is undesirable, reset it with `style="border: 0; padding: 0;"` rather than reaching for a `<div role="group">` ARIA fallback — the native element wins on assistive-technology compatibility.

## Keyboard Interaction

| Key | Action |
|-----|--------|
| `Tab` | Move focus into the radio group |
| `ArrowUp` / `ArrowLeft` | Move focus and selection to the previous radio in the group |
| `ArrowDown` / `ArrowRight` | Move focus and selection to the next radio in the group |
| `Space` | Select the focused radio (when entering the group with `Tab`) |

## Pitfalls

- **Don't omit `name="..."` on a radio group.** All radios in the same group MUST share `name` — otherwise multi-selection is possible and the form posts unexpected data.
- **Don't use a radio group for binary on/off.** A `<x-wirekit::toggle>` is the right pattern; radios are for ≥3 mutually exclusive choices.

## Design Tokens

| Token | Purpose |
| --- | --- |
| `--color-wk-accent` | Inner dot + active border |
| `--color-wk-bg-input` | Circle background |
| `--color-wk-border-strong` / `--color-wk-border-error` | Circle border |
| `--color-wk-ring` | Focus ring |

## Further Reading

- [MDN: `<input type="radio">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio)
- [WAI-ARIA Radio Group Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/)
- [MDN: Grouping radios with `name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio#defining_a_radio_group)
- [MDN: `:checked` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked)
