Skip to content

Toggle Switch Examples

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

Basic

The switch toggles on click, Enter, or Space. It is form-associated, so a label with for targets it like any input.

<div class="switch-basic">
<label for="wifi-switch">Wi-Fi</label>
<sv-toggle-switch id="wifi-switch"></sv-toggle-switch>
</div>
<style>
.switch-basic {
display: flex;
gap: 4em;
align-items: center;
justify-content: center;
padding-block: 1em;
label {
cursor: pointer;
}
}
</style>

Checked and Disabled

checked turns the switch on from markup, and disabled blocks interaction in either state.

<div class="switch-states">
<div class="row">
<label for="switch-checked">Normal</label>
<sv-toggle-switch id="switch-checked" checked></sv-toggle-switch>
</div>
<div class="row">
<label for="switch-disabled">Disabled</label>
<sv-toggle-switch id="switch-disabled" disabled></sv-toggle-switch>
</div>
<div class="row">
<label for="switch-both">Disabled checked</label>
<sv-toggle-switch id="switch-both" checked disabled></sv-toggle-switch>
</div>
</div>
<style>
.switch-states {
display: grid;
gap: 0.75em;
max-inline-size: 16rem;
padding-block: 1em;
margin-inline: auto;
.row {
display: flex;
gap: 0.5em;
align-items: center;
label {
flex: 1;
cursor: pointer;
}
}
}
</style>

Icons

The checked-icon and unchecked-icon slots replace the built-in check and circle with any markup. A blank background slot removes the icons entirely, and the thumb slot fills the thumb itself, here with a pair of crossfading icons.

<div class="switch-icons">
<div class="row">
<label for="switch-text-icons">Text icons</label>
<sv-toggle-switch id="switch-text-icons" class="text-icons" checked>
<strong aria-hidden="true" slot="checked-icon">On</strong>
<span aria-hidden="true" slot="unchecked-icon">Off</span>
</sv-toggle-switch>
</div>
<div class="row">
<label for="switch-no-icons">No icons</label>
<sv-toggle-switch id="switch-no-icons">
<span slot="background"></span>
</sv-toggle-switch>
</div>
<div class="row">
<label for="switch-thumb-icon">Icon in the thumb</label>
<sv-toggle-switch id="switch-thumb-icon" class="thumb-icons">
<span slot="background"></span>
<div class="custom-thumb" aria-hidden="true" slot="thumb">
<svg class="unchecked" viewBox="0 -960 960 960">
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
</svg>
<svg class="checked" viewBox="0 -960 960 960">
<path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z" />
</svg>
</div>
</sv-toggle-switch>
</div>
</div>
<style>
.switch-icons {
display: grid;
gap: 0.75em;
max-inline-size: 16rem;
padding-block: 1em;
margin-inline: auto;
.row {
display: flex;
gap: 0.5em;
align-items: center;
label {
flex: 1;
cursor: pointer;
}
}
.text-icons {
[slot="checked-icon"],
[slot="unchecked-icon"] {
font-size: 0.8em;
}
}
.thumb-icons {
.custom-thumb {
display: grid;
place-items: center;
inline-size: 100%;
block-size: 100%;
svg {
grid-area: 1 / 1;
inline-size: 1.1em;
block-size: 1.1em;
transition:
opacity var(--sv-toggle-switch-dur, 350ms) var(--sv-toggle-switch-ease, cubic-bezier(0.486, 1.45, 0.2, 1)),
scale var(--sv-toggle-switch-dur, 350ms) var(--sv-toggle-switch-ease, cubic-bezier(0.486, 1.45, 0.2, 1));
}
.checked {
opacity: 0;
fill: var(--sv-accent);
scale: 0;
}
.unchecked {
opacity: 1;
fill: var(--sv-gray-5);
scale: 1;
}
}
&:state(--checked) .custom-thumb {
.checked {
opacity: 1;
scale: 1;
}
.unchecked {
opacity: 0;
scale: 0;
}
}
}
}
</style>

Styling

--sv-toggle-switch-sz scales the whole switch, the color tokens tint it, and the thumb and track widths can be set apart for other proportions.

--sv-toggle-switch-sz
--sv-toggle-switch-active-bg
thumb and track widths
<div class="switch-styling">
<div class="variant">
<sv-toggle-switch class="large" aria-label="Large switch" checked></sv-toggle-switch>
<span>--sv-toggle-switch-sz</span>
</div>
<div class="variant">
<sv-toggle-switch class="tinted" aria-label="Green switch" checked></sv-toggle-switch>
<span>--sv-toggle-switch-active-bg</span>
</div>
<div class="variant">
<sv-toggle-switch class="slim" aria-label="Slim switch"></sv-toggle-switch>
<span>thumb and track widths</span>
</div>
</div>
<style>
.switch-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);
}
}
.large {
--sv-toggle-switch-sz: 2.2em;
}
.tinted {
--sv-toggle-switch-active-bg: #22c55e;
}
.slim {
--sv-toggle-switch-thumb-width: 1.2em;
--sv-toggle-switch-thumb-height: 1.2em;
--sv-toggle-switch-width: 3.2em;
}
}
</style>

Animation

--sv-toggle-switch-dur and --sv-toggle-switch-ease reshape the thumb travel, the icon swap, and the color fade together. The default easing is springy; a zero duration makes the switch instant. The reduced motion fallback wins over both.

Reduced Motion

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

--sv-toggle-switch-dur: 800ms
--sv-toggle-switch-ease: ease
--sv-toggle-switch-dur: 0s
<div class="switch-animation">
<div class="variant">
<sv-toggle-switch class="slow" aria-label="Slow switch"></sv-toggle-switch>
<span>--sv-toggle-switch-dur: 800ms</span>
</div>
<div class="variant">
<sv-toggle-switch class="smooth" aria-label="Smooth switch"></sv-toggle-switch>
<span>--sv-toggle-switch-ease: ease</span>
</div>
<div class="variant">
<sv-toggle-switch class="instant" aria-label="Instant switch"></sv-toggle-switch>
<span>--sv-toggle-switch-dur: 0s</span>
</div>
</div>
<style>
.switch-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);
}
}
.slow {
--sv-toggle-switch-dur: 800ms;
}
.smooth {
--sv-toggle-switch-ease: ease;
}
.instant {
--sv-toggle-switch-dur: 0s;
}
}
</style>