CSS-only toggle switch
An accessible on/off toggle switch built from a checkbox and a label — no JavaScript.
On color#6b46ff
Off color#3a3f4f
Size28px
<label class="switch"><input type="checkbox" checked /><span class="slider"></span></label> :root {
--on: #6b46ff;
--off: #3a3f4f;
--size: 28px;
}
.switch {
position: relative;
display: inline-block;
width: calc(var(--size) * 1.8);
height: var(--size);
}
.switch input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
inset: 0;
border-radius: 999px;
background: var(--off);
cursor: pointer;
transition: background 0.2s ease;
}
.slider::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: calc(var(--size) - 6px);
height: calc(var(--size) - 6px);
border-radius: 50%;
background: #fff;
transition: transform 0.2s ease;
}
.switch input:checked + .slider {
background: var(--on);
}
.switch input:checked + .slider::before {
transform: translateX(calc(var(--size) * 0.8));
}
.switch input:focus-visible + .slider {
outline: 2px solid var(--on);
outline-offset: 2px;
} How it works
A real <input type="checkbox"> keeps it accessible and keyboard-operable; it’s
visually hidden, and a <span class="slider"> is styled as the track and knob.
- The track is the
.slider; the knob is its::before. - The
:checked + .slidersibling selector flips the track color and slides the knob with atransform— no JavaScript needed. :focus-visibleadds a keyboard focus ring.
Everything scales from the single --size variable, and the on/off colors are
themeable. Click the switch in the preview to toggle it.