<c-radio-group>
A radio group is a set of mutually exclusive choices where exactly one can be selected, authored as slotted `c-radio` children. Give the group a `label` so the choice it represents is named for every user, and a `hint` describing how to answer.
Usage
Authoring the options
Each option is a <c-radio value="..."> whose default slot is its label —
keep the label text (or richer markup) inside the radio so it stays
clickable and is announced with the control:
<c-radio-group label="Subscription plan" value="free">
<c-radio value="free">Free</c-radio>
<c-radio value="paid">Paid</c-radio>
</c-radio-group>
The radios may sit at any depth inside the default slot, so custom layouts are plain markup — wrap each radio in whatever layout element the design needs:
<c-radio-group label="Subscription plan" value="free">
<div class="card"><c-radio value="free">Free</c-radio></div>
<div class="card"><c-radio value="paid">Paid</c-radio></div>
</c-radio-group>
Text placed next to a radio instead of inside it renders, but is not click-associated or announced — put the label content in the radio's slot.
Value
The group's value is the selected radio's value, matched by strict
string equality; the group holds string values only. Selection is expressed
solely through the group — radios carry no checked state of their own.
Giving two radios the same value is a consumer error: every matching
radio will show as checked.
In Vue, bind with a plain v-model. The group also emits its radios'
bubbling change events onward, with the selected c-radio as the
event target.
Label
The label prop names the group; when richer label content is needed, use
the label slot instead (the prop wins when both are set). The default
slot is exclusively the radios' home — loose text there is not treated as a
label.
Validation
Validation is your job: set valid to false and supply an
error-message explaining why. While invalid without an error-message,
the hint keeps rendering as a hint. The message area reserves its height
(unless hide-details), so an error appearing at runtime doesn't shift the
layout.
Accessibility
The group is announced as a radiogroup named by its label. Keyboard
behavior follows the native pattern: Tab moves into the group (onto the
checked radio, or the first enabled one) and out of it in one stop; the
arrow keys move between radios, selecting as they go, wrapping at the ends
and skipping disabled radios; Space selects the focused radio. Enter is
left to the surrounding form.
Examples
<template>
<div>
<c-radio-group
v-model="plan"
hint="You can change the plan later"
label="Subscription plan"
>
<c-radio value="free">Free</c-radio>
<c-radio value="pro">Pro</c-radio>
<c-radio value="enterprise">Enterprise</c-radio>
</c-radio-group>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const plan = ref('free');
</script>
<template>
<div>
<c-radio-group
v-model="plan"
hint="You can change the plan later"
label="Subscription plan"
>
<div class="plan-option">
<c-radio value="free">Free</c-radio>
</div>
<div class="plan-option">
<c-radio value="pro">Pro</c-radio>
</div>
<div class="plan-option">
<c-radio value="enterprise">Enterprise</c-radio>
</div>
</c-radio-group>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const plan = ref('free');
</script>
<style>
/* The radios are ordinary light DOM: wrap them in your own layout markup and
style it with your own CSS. Keep the label text inside the <c-radio> so the
whole row stays click-associated. */
.plan-option {
border: 1px solid var(--c-border);
border-radius: 0.5rem;
padding: 0 0.75rem;
}
</style>
<template>
<div>
<c-radio-group
v-model="frequency"
hide-details
inline
label="Email frequency"
>
<c-radio value="daily">Daily</c-radio>
<c-radio value="weekly">Weekly</c-radio>
<c-radio value="never">Never</c-radio>
</c-radio-group>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const frequency = ref('weekly');
</script>
<template>
<div>
<c-radio-group
v-model="plan"
hint="You can change the plan later"
label="Subscription plan"
>
<c-radio value="free">Free</c-radio>
<c-radio value="pro">Pro</c-radio>
<c-radio value="enterprise" disabled>Enterprise</c-radio>
</c-radio-group>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const plan = ref('free');
</script>
API reference
<c-radio-group>
A radio group is a set of mutually exclusive choices where exactly one can be selected, authored as slotted `c-radio` children. Give the group a `label` so the choice it represents is named for every user, and a `hint` describing how to answer.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
disabled | disabled | boolean | false | Disable the radio group |
errorMessage | error-message | string | '' | Error message shown in place of the hint while the group is invalid |
hideDetails | hide-details | boolean | false | Hide the hint and error messages |
hint | hint | string | '' | Hint text for the input |
inline | inline | boolean | false | Display radio buttons inline |
label | label | string | '' | Label of the radio group |
required | required | boolean | false | Set as required |
valid | valid | boolean | true | Set the validity of the input |
value | value | null | string | null | Value of the radio group; matched against each radio's `value` by strict string equality |
Events
| Event | Detail | Description |
|---|---|---|
changeValue | string | Fired when a radio is selected, carrying the selected radio's value. Also dispatched as `change-value` — bind that name in Vue templates. |
input | void | Native bubbling input event fired on selection so a plain Vue `v-model` works without the `v-control` directive. No detail. |
update:value | string | v-model contract event fired on selection, carrying the selected radio's value. |
Slots
| Slot | Description |
|---|---|
default | The radios' home: `<c-radio>` children, wrappable in arbitrary layout elements at any depth |
label | Group label content, used when the `label` prop is not set |
CSS parts
Style from outside with c-radio-group::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
root | The radiogroup wrapper element |
label | The group label above the radio buttons |
items | The container the radios are slotted into |
message | The hint/error message area below the radios (always reserved unless `hide-details`) |
<c-radio>
A single radio option inside a `c-radio-group`: a native radio input whose default slot is its clickable, announced label.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
disabled | disabled | boolean | false | Disable the radio button |
value | value | string | '' | Radio button value |
Events
| Event | Detail | Description |
|---|---|---|
change | string | Fired when the radio is selected by the user, carrying its `value`. Bubbles composed so the parent `<c-radio-group>` — whose shadow root a light-DOM event never enters — catches it on its host; a consumer listening on the group hears it too, with the radio as `target`. |
Slots
| Slot | Description |
|---|---|
default | The radio's label content, rendered inside the shadow `<label>` so it stays click-associated and announced |
CSS parts
Style from outside with c-radio::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
root | The `<label>` row wrapping the input, indicator and label content |
indicator | The radio ring itself; the selection dot is its `::after` and the keyboard focus ring its `::before` — all three follow `currentColor`, so `color` recolours the whole indicator, focus ring included |
content | Wrapper around the slotted label content |
Custom states
Select on the host's live state with c-radio:state(name) — combine with ::part() for per-state styling, e.g. c-radio:state(checked)::part(indicator).
| State | Description |
|---|---|
checked | Present while the radio is the selected option (standalone or group-driven) |
disabled | Present while the radio is disabled, by its own prop or by its group |