RadioGroup
A group of radio buttons for single selection. Supports list/card variants, horizontal/vertical orientation, legend, descriptions, and controlled state.
aria-* props now land on the radio group element instead of the wrapper around it, so an aria-label names the control. class, style and data-* stay on the wrapper. A selector or test query that reached the control through the wrapper needs the change below./* Before 2.9.0: the attribute sat on the wrapper */
[aria-label="Plan"] [role="radiogroup"] {
outline-offset: 4px;
}
/* From 2.9.0: it is on the control. In a test,
getByRole('radiogroup', { name: 'Plan' }) now finds it */
[role="radiogroup"][aria-label="Plan"] {
outline-offset: 4px;
}Playground
Experiment with different props in real-time.
Basic Usage
Pass items and use bind:value.
<script lang="ts">
import { RadioGroup } from 'sv5ui';
let plan = $state('standard');
const plans = [
{ value: 'starter', label: 'Starter' },
{ value: 'standard', label: 'Standard' },
{ value: 'premium', label: 'Premium' }
];
</script>
<RadioGroup items={plans} bind:value={plan} />With Descriptions
Add per-item descriptions.
Delivered within 1-2 business days
Delivered within 3-5 business days
Delivered within 7-10 business days
<RadioGroup items={[
{ value: 'express', label: 'Express', description: 'Delivered within 1-2 business days' },
{ value: 'standard', label: 'Standard', description: 'Delivered within 3-5 business days' },
{ value: 'economy', label: 'Economy', description: 'Delivered within 7-10 business days' }
]} bind:value />Variants
2 visual styles.
<!-- List variant (default) -->
<RadioGroup variant="list" legend="List" {items} bind:value />
<!-- Card variant -->
<RadioGroup variant="card" legend="Card" {items} bind:value />Indicator Position
Control where the radio indicator appears.
<RadioGroup indicator="start" legend="Start" {items} bind:value />
<RadioGroup indicator="end" legend="End" {items} bind:value />
<RadioGroup indicator="hidden" legend="Hidden" {items} bind:value />Orientation
Vertical or horizontal layout.
<!-- Vertical (default) -->
<RadioGroup orientation="vertical" {items} bind:value />
<!-- Horizontal -->
<RadioGroup orientation="horizontal" {items} bind:value />Sizes
5 sizes.
<RadioGroup size="xs" {items} bind:value />
<RadioGroup size="sm" {items} bind:value />
<RadioGroup size="md" {items} bind:value />
<RadioGroup size="lg" {items} bind:value />
<RadioGroup size="xl" {items} bind:value />Colors
8 semantic color schemes.
<RadioGroup color="primary" {items} bind:value />
<RadioGroup color="success" {items} bind:value />
<RadioGroup color="warning" {items} bind:value />
<RadioGroup color="error" {items} bind:value />Legend
Add an accessible legend with legend and required.
<RadioGroup items={plans} bind:value legend="Choose your plan" required />Disabled
Disable entire group or individual items.
<!-- Disable entire group -->
<RadioGroup disabled {items} bind:value />
<!-- Disable individual items -->
<RadioGroup items={[
{ value: 'free', label: 'Free' },
{ value: 'pro', label: 'Pro' },
{ value: 'enterprise', label: 'Enterprise', disabled: true }
]} bind:value />Loading
Show spinner state.
<RadioGroup loading {items} bind:value />Card Grid
Combine card variant with horizontal orientation.
5GB storage, basic features
50GB storage, advanced analytics
Unlimited storage, collaboration
<RadioGroup
variant="card"
orientation="horizontal"
legend="Select a plan"
items={[
{ value: 'free', label: 'Free', description: '5GB storage, basic features' },
{ value: 'pro', label: 'Pro', description: '50GB storage, advanced analytics' },
{ value: 'team', label: 'Team', description: 'Unlimited storage, collaboration' }
]}
bind:value
/>Controlled
Use bind:value for two-way binding.
Selected: standard
<script lang="ts">
import { RadioGroup } from 'sv5ui';
let value = $state('standard');
</script>
<RadioGroup {items} bind:value />
<p>Selected: {value}</p>Accessibility
A group with a legend is named by it. Without one, pass aria-label. It lands on the radio group element, where assistive technology looks for it, along with the other naming and describing attributes: aria-label, aria-labelledby, aria-describedby, aria-invalid, aria-required, aria-errormessage and aria-details. class, style, data-* and aria-hidden stay on the wrapper.
<!-- No legend: name the group yourself -->
<RadioGroup aria-label="Plan" items={plans} value="pro" orientation="horizontal" />onfocus and onblur describe the group as a whole: each runs once, when focus enters the group and when it leaves, not on every move between options. Inside a FormField the field label names a group that has no legend, and the description is announced.
UI Slots
Use the ui prop to override classes on internal elements.
| Slot | Description |
|---|---|
root | Outermost wrapper |
fieldset | Fieldset wrapping the group |
legend | Legend element for the fieldset |
item | Each radio item row |
base | The radio circle element |
container | Wrapper around the radio indicator |
indicator | Inner dot when selected |
loadingIcon | Loading spinner icon |
wrapper | Wraps label and description |
label | Label text |
description | Description text |
Snippets
| Snippet | Description |
|---|---|
legendSlot | Custom legend rendering |
labelSlot | Custom label for each item |
descriptionSlot | Custom description for each item |
Props
| Prop | Type | Default |
|---|---|---|
items | RadioGroupItem[] | [] |
value | string | '' |
onValueChange | (value) => void | - |
variant | 'list' | 'card' | 'list' |
indicator | 'start' | 'end' | 'hidden' | 'start' |
orientation | 'vertical' | 'horizontal' | 'vertical' |
color | ColorType | 'primary' |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
legend | string | - |
loading | boolean | false |
disabled | boolean | false |
required | boolean | false |
loop | boolean | true |
name | string | - |
id | string | - |
ref | HTMLElement | null | null |
class | string | - |
ui | Record<Slot, Class> | - |
Item Props
| Prop | Type | Default |
|---|---|---|
value | string | - |
label | string | - |
description | string | - |
disabled | boolean | false |