Radio
A form control for single-select lists. Multiple radios sharing the same name form a mutually exclusive group.
Basic Usage
With Hint
€9 billed each month
€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.
Sizes
sm, md (default), and lg scale the circle and its inner dot together, matching the size prop on Checkbox and Toggle.
Disabled
With Error
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.
<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" />
Bound to a property declared int, float or an enum backed by int, each radio is read as a number: wire:model.live goes out as wire:model.. A radio gives its value as text, and Livewire would take the server's typed answer for a change made on the server and set the group back to an earlier choice while a newer one is on its way. A property without a type, or a string one, gets the text.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string|null | null |
Text next to the radio |
hint |
string|null | null |
Helper text below |
help |
string|null | null |
An explanation of the field, shown in a tooltip from a question mark beside the label and read as the field's description. Needs a label to stand beside. See Help beside the label |
error |
string|null | null |
Error message (also triggers aria-invalid) |
groupError |
bool | true |
Take the surrounding field.set's group error, or decline it with :group-error="false". A control that belongs to the group but neither causes the rejection nor can resolve it is then neither marked aria-invalid by the group nor described by its message; its own error, its own hint and the set's hint are unaffected |
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 |
announceError |
bool|null | null |
Render the error message in an aria-live="polite" region so a validation error that appears after render reaches a screen reader. Precedence: this prop, then the parent <x-wirekit::form :announce-errors>, then config('wirekit.a11y.announce_error'). Set false when the page runs its own live region. |
The id is auto-generated as {name}-{value} when both are given, ensuring unique IDs within a radio group.
Slots
| Slot | Description |
|---|---|
labelAfter |
Markup after the label and its help question mark, in the same row and outside both, such as a badge that says a setting is not decided yet. See A Mark After the Label. |
Accessibility
- Native
<input type="radio">— browser provides full keyboard support:- Arrow keys navigate between options within the same
namegroup - Space selects the focused radio
- Tab moves focus to the first/checked radio of the next group
- Arrow keys navigate between options within the same
- The focus ring follows the arrow keys in every browser. Safari, and every browser on iOS, does not count the focus an arrow key moves as keyboard focus, so WireKit's scripts note when an arrow key brings the focus to a radio, and the ring follows it there too, on the circle and on a card. Without
@wirekitScriptson the page, Safari draws the ring afterTaband not after an arrow key. - The visual circle and inner dot are decorative (
aria-hidden="true"). - For a screen-reader-announced group label, wrap related radios in
<x-wirekit::. It renders a nativefield. set> <fieldset>with its<legend>as the first child, the WCAG-recommended pattern for grouped form controls that is announced before each radio in the set, and it resets the fieldset's default border and spacing:
<x-wirekit::field.set legend="Choose a plan" name="plan">
<x-wirekit::radio name="plan" value="free" label="Free" />
<x-wirekit::radio name="plan" value="pro" label="Pro" />
</x-wirekit::field.set>
With name, the set shows a validation error for plan once, above the options, and every radio points at it. Radios outside a set each render that message under themselves.
- A label in another language than the page, such as a language's own name, takes
lang. It goes on the label, which holds the words and the radio, so a screen reader says them in that language's voice:
<x-wirekit::field.set legend="Language" name="language">
<x-wirekit::radio name="language" value="de" label="Deutsch" lang="de" />
<x-wirekit::radio name="language" value="fr" label="Français" lang="fr" />
</x-wirekit::field.set>
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 sharename— otherwise multi-selection is possible and the form posts unexpected data. - Don't use a radio group for binary on/off. A
<x-wirekit::is the right pattern; radios are for ≥3 mutually exclusive choices.toggle>
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 |
Personalization blocks
Three visual zones are resolvable through WireKit:: / WireKit::, and a config override under components. reaches the same keys:
| Block key | Applies to |
|---|---|
radio.base |
The visible circle — its border, fill and focus ring |
radio.label |
The wrapping <label> — its display mode, gap and padding (both variants) |
radio.text |
The visible label text |
The label is inline-flex, so it is as wide as its content. To make a radio a full-width row with a code or a count at its edge, swap the display mode through the label block and let the text take the rest of the row through the text block, as on a checkbox.