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.
<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>.
<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.
<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.
<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.
Your system is set to reduce motion, so this animation will not play for you.
<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>