ComponentsSwitch
Switch
atomA 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; }