Home / Snippets / UI Components /
Switch / toggle
iOS-style toggle — checkbox underneath, CSS on top, zero JS.
Quick implementation
/* HTML: <label><input type="checkbox" class="toggle-input" /><span class="toggle-track"></span> Label</label> */
.toggle-input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.toggle-track {
position: relative;
display: inline-block;
width: 2.75rem;
height: 1.5rem;
background: oklch(0.35 0.02 260);
border-radius: 999px;
cursor: pointer;
transition: background 0.2s ease-out;
}
.toggle-track::after {
content: '';
position: absolute;
top: 0.2rem;
left: 0.2rem;
width: 1.1rem;
height: 1.1rem;
background: white;
border-radius: 50%;
transition: transform 0.2s ease-out;
}
.toggle-input:checked + .toggle-track {
background: oklch(0.52 0.22 265);
}
.toggle-input:checked + .toggle-track::after {
transform: translateX(1.25rem);
}
.toggle-input:focus-visible + .toggle-track {
outline: 2px solid oklch(0.72 0.19 265);
outline-offset: 2px;
}
.toggle-input:disabled + .toggle-track {
opacity: 0.4;
cursor: not-allowed;
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building a toggle switch component.
Goal: An iOS-style toggle switch using only HTML and CSS — no JavaScript for the toggle behavior.
Technical constraints:
- Use a hidden checkbox input as the state mechanism.
- Track: inline-block, pill shape with border-radius: 999px.
- Thumb: ::after pseudo-element, circle, slides right on :checked via translateX.
- Use oklch() for all colors, not hex or rgba().
- Focus-visible on the input should outline the track (via + sibling selector).
- Transition: background and transform on 0.2s ease-out.
Framework variant (pick one):
A) Vanilla HTML + CSS only.
B) React component — accept checked, onChange, disabled, and label props.
Edge cases to handle:
- Disabled state: reduced opacity, not-allowed cursor on track.
- The checkbox must remain in the DOM for form submission and screen readers.
- Add role="switch" for better screen reader semantics.
Return HTML + CSS.
Why this matters in 2026
Toggle switches communicate binary on/off states more intuitively than checkboxes for settings like dark mode, notifications, and feature flags. Building one without JavaScript keeps the bundle lean and the behavior predictable — the native checkbox handles state, forms, and keyboard interaction for free.
The logic
The checkbox is visually hidden with opacity: 0 but remains in the DOM and focusable. The .toggle-track is the visible pill shape. Its ::after pseudo-element is the sliding thumb. On :checked, the adjacent sibling selector (+) changes the track background and moves the thumb with translateX. The transition on both background and transform creates the smooth slide animation.
Accessibility & performance
Wrap the input and track in a <label> to make the entire row clickable. Add role="switch" to the checkbox for screen readers that support it. The focus ring appears on the track (not the hidden input) via the :focus-visible + .toggle-track selector. All transitions use compositor-only properties for buttery-smooth rendering.