Toggle
A CSS-only toggle switch built on a native checkbox, aria-checked state, or a JavaScript-driven active class. Pick a variant and a palette; the track, thumb, and transition states are already wired in.
At a glance
Playground
Controls
Preview
Codehtml
<label class="toggle-solid/primary">
<input type="checkbox" checked readonly />
</label>
Toggle
component
Applies toggle styling using the selected variant and tone palette. Checked toggles render with the chosen palette, while unchecked toggles automatically fall back to palette-disabled.
Pattern
toggle-{variant}[/{palette}]
Variant
solid/soft/ghost/outline
Palette
primary/secondary/accent/surface/success/danger/disabled/default:inherited
Solid
Primary
Secondary
Accent
Surface
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Primary
</span>
<label class="toggle-solid/primary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Secondary
</span>
<label class="toggle-solid/secondary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Accent
</span>
<label class="toggle-solid/accent">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Surface
</span>
<label class="toggle-solid/surface">
<input type="checkbox" checked readonly />
</label>
Soft
Primary
Secondary
Accent
Surface
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Primary
</span>
<label class="toggle-soft/primary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Secondary
</span>
<label class="toggle-soft/secondary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Accent
</span>
<label class="toggle-soft/accent">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Surface
</span>
<label class="toggle-soft/surface">
<input type="checkbox" checked readonly />
</label>
Outline
Primary
Secondary
Accent
Surface
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Primary
</span>
<label class="toggle-outline/primary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Secondary
</span>
<label class="toggle-outline/secondary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Accent
</span>
<label class="toggle-outline/accent">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Surface
</span>
<label class="toggle-outline/surface">
<input type="checkbox" checked readonly />
</label>
Ghost
Primary
Secondary
Accent
Surface
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Primary
</span>
<label class="toggle-ghost/primary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Secondary
</span>
<label class="toggle-ghost/secondary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Accent
</span>
<label class="toggle-ghost/accent">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Surface
</span>
<label class="toggle-ghost/surface">
<input type="checkbox" checked readonly />
</label>
--toggle-h
Override the track height inline to make the control smaller or larger without introducing another variant.
1.25rem
Default
2.5rem
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
1.25rem
</span>
<label class="toggle-solid/primary [--toggle-h:1.25rem]">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Default
</span>
<label class="toggle-solid/primary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
2.5rem
</span>
<label class="toggle-solid/primary [--toggle-h:2.5rem]">
<input type="checkbox" checked readonly />
</label>
--toggle-p
Adjust the inner padding between the track edge and the thumb. Larger values create more inset spacing.
0
Default
0.35rem
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
0
</span>
<label class="toggle-soft/secondary [--toggle-p:0]">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Default
</span>
<label class="toggle-soft/secondary">
<input type="checkbox" checked readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
0.35rem
</span>
<label class="toggle-soft/secondary [--toggle-p:0.35rem]">
<input type="checkbox" checked readonly />
</label>
Toggle Triggers
Label + Checkbox
The native checkbox pattern needs no extra JavaScript. The checked input drives the on state automatically.
Unchecked
Checked
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Unchecked
</span>
<label class="toggle-soft/primary">
<input type="checkbox" readonly />
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Checked
</span>
<label class="toggle-soft/primary">
<input type="checkbox" checked readonly />
</label>
Button + aria-checked
Use aria-checked on a custom control when you need button semantics or your own interaction layer.
aria-checked="false"
aria-checked="true"
Codehtml
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
aria-checked="false"
</span>
<button
type="button"
role="switch"
aria-checked="false"
aria-label='aria-checked="false"'
class="toggle-outline/accent"
></button>
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
aria-checked="true"
</span>
<button
type="button"
role="switch"
aria-checked="true"
aria-label='aria-checked="true"'
class="toggle-outline/accent"
></button>
.toggle-active class
For JavaScript-managed state, add .toggle-active to the toggle root to force the on state.
Without .toggle-active
With .toggle-active
Codehtml
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
Without .toggle-active
</span>
<button
type="button"
role="switch"
aria-checked="false"
aria-label="Without .toggle-active"
class="toggle-ghost/surface"
></button>
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
With .toggle-active
</span>
<button
type="button"
role="switch"
aria-checked="false"
aria-label="With .toggle-active"
class="toggle-active toggle-ghost/surface"
></button>
Thumb Slot
slot
Add a child with [data-slot="thumb"] to replace the default thumb rendering while keeping the same toggle behavior.
thumb
[data-slot="thumb"]
Custom Thumb
Supply your own thumb content to add glyphs or state cues without rewriting the toggle track.
Unchecked
Checked
Codehtml
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Unchecked
</span>
<label class="toggle-outline/surface">
<input type="checkbox" readonly />
<span data-slot="thumb">
<span
class="flex h-full items-center justify-center rounded-full text-[0.625rem] font-semibold"
>
L
</span>
</span>
</label>
<span
class="text-sm font-medium tracking-[0.01em] text-on-tone-50-surface/78"
>
Checked
</span>
<label
class="toggle-outline/surface toggle-on:text-white toggle-off:palette-surface"
>
<input type="checkbox" checked readonly />
<span data-slot="thumb">
<span
class="flex h-full items-center justify-center rounded-full text-[0.625rem] font-semibold"
>
D
</span>
</span>
</label>
Custom Variants
custom-variant
The toggle exposes state variants for styling checked and unchecked states directly in utility classes.
toggle-on:
:has(input:checked), [aria-checked='true'], .toggle-active
toggle-off:
&:not(:is(:has(input:checked), [aria-checked='true'], .toggle-active))
toggle-on: and toggle-off:
Use the generated state variants to target thumb and track styling based on whether the toggle is on or off.
toggle-off:palette-surface
toggle-on:text-white
Codehtml
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
toggle-off:palette-surface
</span>
<label
class="toggle-outline/surface toggle-on:text-white toggle-off:palette-surface"
>
<input type="checkbox" readonly />
<span data-slot="thumb">
<span
class="flex h-full items-center justify-center rounded-full text-[0.625rem] font-semibold"
>
L
</span>
</span>
</label>
<span
class="font-mono text-xs tracking-normal text-tone-500-accent"
>
toggle-on:text-white
</span>
<label
class="toggle-outline/surface toggle-on:text-white toggle-off:palette-surface"
>
<input type="checkbox" checked readonly />
<span data-slot="thumb">
<span
class="flex h-full items-center justify-center rounded-full text-[0.625rem] font-semibold"
>
D
</span>
</span>
</label>
API Reference
Classes
componenttoggle-{variant}[/{palette}]
component
Applies toggle styling using the selected variant and tone palette. Checked toggles render with the chosen palette, while unchecked toggles automatically fall back to palette-disabled.
Variant
solid/soft/ghost/outline
Paletteprimary/secondary/accent/surface/success/danger/disabled/default:inherited
Slots
slotthumb
slot
Add a child with [data-slot="thumb"] to replace the default thumb rendering while keeping the same toggle behavior.
selector:[data-slot="thumb"]
Custom Variants
custom-varianttoggle-on:
custom-variant
The toggle exposes state variants for styling checked and unchecked states directly in utility classes.
selector::has(input:checked), [aria-checked='true'], .toggle-active
custom-varianttoggle-off:
custom-variant
The toggle exposes state variants for styling checked and unchecked states directly in utility classes.
selector:&:not(:is(:has(input:checked), [aria-checked='true'], .toggle-active))
CSS Variables
css-variable--toggle-h
css-variable
Height of the toggle track.
default:1.75rem
css-variable--toggle-p
css-variable
Inner padding between the track edge and the thumb.
default:calc(var(--h) * 0.125)