Home / Snippets / UI Components /

Switch / toggle

iOS-style toggle — checkbox underneath, CSS on top, zero JS.

Widely Supported
uino-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.