ComponentsSwitch

Switch

atom

A binary toggle with a shape-locked round knob and the kit’s motion.

Notification settings

Choose what we can reach you about.

Push notifications
Deals, order updates, replies
Email digest
A weekly summary every Monday
SMS alerts
Only security & login events
Security alerts
Always on — can't be disabled

Recipe CSS

This is the exact CSS your kit ships for Switch — token-driven, so it re-themes with every knob. Get it (and the rest) from the configurator's Use kit panel.

/* === Switch / toggle ===
 * Default size is Stature-driven (compact 26×14, balanced 32×18, bold 40×22).
 * .toggle--sm / .toggle--lg are explicit overrides for cases where you want
 * a smaller/bigger toggle than the stature default (e.g. a Bold-stature
 * settings page with a sm toggle inside a table cell). */
/* Toggle uses two local CSS vars (--tog-w, --tog-h) so any size variant
 * just overrides those two values and the knob + translate adapt via calc.
 * No more hardcoded "12px knob" that breaks when stature scales the track. */
.toggle {
  --tog-w: var(--k-toggle-w-default, 32px);
  --tog-h: var(--k-toggle-h-default, 18px);
  width: var(--tog-w);
  height: var(--tog-h);
  /* Off-track is a clearly-recessed muted fill (shadcn bg-input) so the OFF
     state reads unmistakably off, not just a faint tint of the card. */
  background: var(--k-track, var(--k-surface-sunken));
  border-radius: 999px;
  border: 1px solid var(--k-border);
  display: inline-flex;
  align-items: center;
  padding: var(--k-s-2);
  cursor: pointer;
  /* Fast easing for microinteraction — toggle is a tap, not a transition */
  transition: background var(--k-dur-fast, 120ms) var(--k-ease, ease),
              border-color var(--k-dur-fast, 120ms) var(--k-ease, ease);
}
/* Hover affordance (B1) — the OFF track firms its edge, the ON track lifts a
 * touch, so the toggle reads as live before you click it (the gap shadcn fills
 * with a subtle hover). Suppressed on the disabled modifier (pointer-events:none
 * already blocks it, but be explicit). */
.toggle:not(.toggle--disabled):hover { border-color: var(--k-state-border, var(--k-fg-faint)); }
.toggle--on:not(.toggle--disabled):hover,
.toggle[aria-checked="true"]:not(.toggle--disabled):hover,
.toggle[aria-pressed="true"]:not(.toggle--disabled):hover { filter: brightness(1.05); }
/* Knob is always (track-h − 6px): leaves 2px padding + 1px border breathing room
 * on top and bottom regardless of stature. ON-translate is (track-w − track-h)
 * which lands the knob exactly at the right edge with matching inset. */
.toggle__knob {
  width: calc(var(--tog-h) - 6px);
  height: calc(var(--tog-h) - 6px);
  background: var(--k-surface);
  border-radius: 50%;
  /* Spring overshoot on the knob translation — the signature "click" feel.
   * Box-shadow uses the tactile token so the knob reads as a discrete
   * physical object riding the track. */
  transition: transform var(--k-dur) var(--k-ease-spring, cubic-bezier(.34,1.56,.64,1));
  box-shadow: var(--k-shadow-tactile, var(--k-shadow-sm));
}
/* ON state is driven by the class OR the aria attribute, so a consumer that
 * correctly sets role=switch + aria-checked (the a11y truth) gets the visual for
 * free — no silent class/aria desync. Same model as .btn--toggle[aria-pressed]. */
.toggle--on, .toggle[aria-checked="true"], .toggle[aria-pressed="true"] { background: var(--k-fill, var(--k-primary)); border-color: var(--k-primary); }
.toggle--on .toggle__knob,
.toggle[aria-checked="true"] .toggle__knob,
.toggle[aria-pressed="true"] .toggle__knob {
  transform: translateX(calc(var(--tog-w) - var(--tog-h)));
  background: var(--k-primary-fg);
}
/* Disabled — the global :disabled rule excludes .toggle, so own it here (a
 * disabled-ON switch must keep its colour). aria-invalid gets a danger edge. */
.toggle:disabled, .toggle[aria-disabled="true"] { opacity: var(--k-disabled-opacity, 0.55); pointer-events: none; cursor: not-allowed; }
.toggle[aria-invalid="true"] { border-color: var(--k-input-error-border); }

/* Toggle size modifiers — explicit overrides for cases where you want a
 * smaller/bigger toggle than the stature default. Only --tog-w / --tog-h
 * change; the knob math adapts automatically. */
.toggle--sm { --tog-w: 26px; --tog-h: 14px; }
.toggle--lg { --tog-w: 40px; --tog-h: 22px; }
/* Disabled toggle — state parity with .slider--disabled / :disabled inputs:
 * muted + not-allowed + no pointer. Combine with .toggle--on for disabled-on.
 * (Add aria-disabled="true" on the control for the a11y half.) */
.toggle--disabled { opacity: var(--k-disabled-opacity, 0.55); pointer-events: none; cursor: not-allowed; }