Skip to content

Radio Examples

The examples below assume that you have imported the component and set up the theme.

Basic

Radios with the same name form one group, and the checked one submits its value with the form. Tab stops once on the group, and the arrow keys move the choice.

Shipping Standard Express Overnight
shipping=standard
<form class="radio-basic">
<fieldset role="radiogroup">
<legend>Shipping</legend>
<sv-radio checked name="shipping" value="standard">Standard</sv-radio>
<sv-radio name="shipping" value="express">Express</sv-radio>
<sv-radio name="shipping" value="overnight">Overnight</sv-radio>
</fieldset>
<output>shipping=standard</output>
</form>
<script type="application/x-typescript" module>
const form = document.querySelector<HTMLFormElement>(".radio-basic")!;
const output = form.querySelector("output")!;
form.addEventListener("change", () => {
output.textContent = `shipping=${new FormData(form).get("shipping")}`;
});
</script>
<style>
.radio-basic {
display: grid;
gap: 1em;
justify-items: center;
padding-block: 1em;
fieldset {
display: flex;
flex-direction: column;
gap: 0.6em;
border: none;
}
legend {
margin-block-end: 0.4em;
font-weight: 600;
}
output {
font-family: monospace;
font-size: 0.85em;
color: var(--sv-gray-3);
}
}
</style>

Checked and Disabled

checked picks the starting radio, and disabled takes a radio out of the group's keyboard order. A container with role="radiogroup" and a label names a group that has no <fieldset>.

Free Pro Team
<div class="radio-states" aria-label="Plan" role="radiogroup">
<sv-radio checked name="plan" value="free">Free</sv-radio>
<sv-radio name="plan" value="pro">Pro</sv-radio>
<sv-radio disabled name="plan" value="team">Team</sv-radio>
</div>
<style>
.radio-states {
display: flex;
flex-wrap: wrap;
gap: 1.5em;
justify-content: center;
padding-block: 1em;
}
</style>

Custom Dot

The dot slot puts any markup in the center of the ring. It is always shown, so this example scales it in from :state(--checked), with a thinner checked border to leave room for it.

Monthly Yearly
<div class="radio-dot" aria-label="Billing" role="radiogroup">
<sv-radio checked name="billing" value="monthly">
<!-- Check -->
<svg aria-hidden="true" slot="dot" viewBox="0 0 24 24">
<path d="M5 12.5l4.5 4.5L19 7.5" />
</svg>
Monthly
</sv-radio>
<sv-radio name="billing" value="yearly">
<!-- Check -->
<svg aria-hidden="true" slot="dot" viewBox="0 0 24 24">
<path d="M5 12.5l4.5 4.5L19 7.5" />
</svg>
Yearly
</sv-radio>
</div>
<style>
.radio-dot {
display: flex;
gap: 1.5em;
justify-content: center;
padding-block: 1em;
sv-radio {
--sv-radio-sz: 1.4em;
--sv-radio-active-bdr-sz: 2px;
}
svg {
inline-size: 60%;
block-size: 60%;
fill: none;
stroke: var(--sv-accent, AccentColor);
stroke-width: 3;
stroke-linecap: round;
stroke-linejoin: round;
scale: 0;
transition: scale 200ms ease-out;
}
sv-radio:state(--checked) svg {
scale: 1;
}
@media (prefers-reduced-motion: reduce) {
svg {
transition-duration: 0s;
}
}
}
</style>

Styling

--sv-radio-sz scales the ring, the color tokens tint it, and --sv-radio-active-bdr-sz sets how thick it gets when checked.

--sv-radio-sz
--sv-radio-active-bdr-clr
--sv-radio-active-bdr-sz
<div class="radio-styling">
<div class="variant">
<div class="pair large" aria-label="Large" role="radiogroup">
<sv-radio aria-label="First" checked name="large" value="1"></sv-radio>
<sv-radio aria-label="Second" name="large" value="2"></sv-radio>
</div>
<span>--sv-radio-sz</span>
</div>
<div class="variant">
<div class="pair tinted" aria-label="Green" role="radiogroup">
<sv-radio aria-label="First" checked name="tinted" value="1"></sv-radio>
<sv-radio aria-label="Second" name="tinted" value="2"></sv-radio>
</div>
<span>--sv-radio-active-bdr-clr</span>
</div>
<div class="variant">
<div class="pair thin" aria-label="Thin" role="radiogroup">
<sv-radio aria-label="First" checked name="thin" value="1"></sv-radio>
<sv-radio aria-label="Second" name="thin" value="2"></sv-radio>
</div>
<span>--sv-radio-active-bdr-sz</span>
</div>
</div>
<style>
.radio-styling {
display: grid;
gap: 1.5em;
justify-items: center;
padding-block: 1em;
.variant {
display: grid;
gap: 0.4em;
justify-items: center;
span {
font-size: 0.8em;
color: var(--sv-gray-3);
}
}
.pair {
display: flex;
gap: 1em;
}
sv-radio {
--sv-radio-sz: 1.6em;
}
.large sv-radio {
--sv-radio-sz: 2.6em;
}
.tinted sv-radio {
--sv-radio-active-bdr-clr: #22c55e;
}
.thin sv-radio {
--sv-radio-active-bdr-sz: 2px;
}
}
</style>

Animation

--sv-radio-dur and --sv-radio-ease shape how the ring thickens and changes color. The default easing is springy, and a zero duration makes the radio instant.

Reduced Motion

Your system is set to reduce motion, so this animation will not play for you.

--sv-radio-dur: 800ms
--sv-radio-ease: ease
--sv-radio-dur: 0s
<div class="radio-animation">
<div class="variant" aria-label="Slow" role="radiogroup">
<div class="pair">
<sv-radio class="slow" aria-label="Slow first" checked name="slow"></sv-radio>
<sv-radio class="slow" aria-label="Slow second" name="slow"></sv-radio>
</div>
<span>--sv-radio-dur: 800ms</span>
</div>
<div class="variant" aria-label="Smooth" role="radiogroup">
<div class="pair">
<sv-radio class="smooth" aria-label="Smooth first" checked name="smooth"></sv-radio>
<sv-radio class="smooth" aria-label="Smooth second" name="smooth"></sv-radio>
</div>
<span>--sv-radio-ease: ease</span>
</div>
<div class="variant" aria-label="Instant" role="radiogroup">
<div class="pair">
<sv-radio class="instant" aria-label="Instant first" checked name="instant"></sv-radio>
<sv-radio class="instant" aria-label="Instant second" name="instant"></sv-radio>
</div>
<span>--sv-radio-dur: 0s</span>
</div>
</div>
<style>
.radio-animation {
display: grid;
gap: 1.5em;
justify-items: center;
padding-block: 1em;
.variant {
display: grid;
gap: 0.4em;
justify-items: center;
span {
font-size: 0.8em;
color: var(--sv-gray-3);
}
}
.pair {
display: flex;
gap: 1em;
}
sv-radio {
--sv-radio-sz: 1.8em;
}
.slow {
--sv-radio-dur: 800ms;
}
.smooth {
--sv-radio-ease: ease;
}
.instant {
--sv-radio-dur: 0s;
}
@media (prefers-reduced-motion: reduce) {
.slow {
--sv-radio-dur: 0s;
}
}
}
</style>