Components
Switch
Toggle control built from native checkbox with switch role.
The switch restyles <input type="checkbox" role="switch"> into a sliding toggle. Bind with checked, name, and value.
Default
Loading components…
<fieldset class="ui-field-group"> <legend>Notifications</legend> <div class="flex flex-col gap-xs"> <label class="ui-field" data-orientation="horizontal"> <input type="checkbox" role="switch" name="notifications-email" /> <span data-slot="field-label">Email notifications</span> </label> <label class="ui-field" data-orientation="horizontal"> <input type="checkbox" role="switch" name="notifications-2fa" checked /> <span data-slot="field-label">Two-factor authentication</span> </label> </div></fieldset>/** * switch.css — Switch (input[type="checkbox"][role="switch"]) * * @layer variables, reset * @requires layers.css, _variables.css * @uses appearance: none on input[role="switch"] * @uses label:has(> input[role="switch"])::after — thumb drawn on label (no JS) * @uses oklch(from …) — checked/hover track tints * @tokens --ui-switch-* (@layer variables; consumed in @layer reset) */@layer variables { :root { --ui-switch-track-width: var(--step-7); --ui-switch-track-padding: var(--step-0_5); --ui-switch-track-height: calc( var(--ui-switch-track-width) / 2 + var(--ui-switch-track-padding) ); --ui-switch-thumb: var(--white); --ui-switch-thumb-size: calc( (var(--ui-switch-track-width) / 2) - var(--ui-switch-track-padding) ); --ui-switch-thumb-shadow: var(--shadow-xs); --ui-switch-thumb-translate: var(--ui-switch-track-padding), -50%; --ui-switch-thumb-scale: 1; --ui-switch-track-background: var(--muted); --ui-switch-track-background--hover: oklch(from var(--muted) l c h / calc(alpha + 0.04)); --ui-switch-track-background--checked: var(--primary); --ui-switch-track-background--checked-hover: oklch(from var(--primary) l c h / 0.9); --ui-switch-radius: var(--radius-full); }}@layer reset { /* =========================================================================== SWITCH — native <input type="checkbox" role="switch"> internals - In the reset layer so component overrides win. =========================================================================== */ :where(label:has(> input[role="switch"])) { position: relative; &::after { content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 0; inline-size: var(--ui-switch-thumb-size); block-size: var(--ui-switch-thumb-size); border-radius: var(--radius-full); background-color: var(--ui-switch-thumb); box-shadow: var(--ui-switch-thumb-shadow); pointer-events: none; transform: translate(var(--ui-switch-thumb-translate)) scale(var(--ui-switch-thumb-scale)); transition: box-shadow var(--default-transition-duration) var(--default-transition-timing-function), transform var(--default-transition-duration) var(--default-transition-timing-function); z-index: 1; } &:has(> input[role="switch"]:checked)::after { --ui-switch-thumb-translate: calc(var(--ui-switch-track-padding) + var(--ui-switch-thumb-size)), -50%; } &:has(> input[role="switch"]:hover):after { --ui-switch-thumb-scale: 1.1; } &:has(> input[role="switch"]:active):after { --ui-switch-thumb-shadow: none; --ui-switch-thumb-scale: 0.98; } } :where(label:has(> input[role="switch"]:disabled)) { opacity: 0.5; cursor: not-allowed; } :where(label:has(> input[role="switch"]:disabled) > input[role="switch"]) { opacity: 1; } :where(input[role="switch"]) { appearance: none; flex-shrink: 0; inline-size: var(--ui-switch-track-width); block-size: var(--ui-switch-track-height); background-color: var(--ui-switch-track-background); border-radius: var(--ui-switch-radius); cursor: pointer; /* ring renders as box-shadow; transparent outline twin keeps focus visible in forced-colors / high-contrast modes */ --_ring-offset-width: 0px; --_ring-width: 0px; box-shadow: 0 0 0 var(--_ring-offset-width) var(--ring-offset-color), 0 0 0 calc(var(--_ring-offset-width) + var(--_ring-width)) var(--_ring, var(--ring)); outline: var(--outline-width) var(--outline-style) transparent; outline-offset: var(--outline-offset); transition: var(--default-transition); } :where(input[role="switch"]:hover) { background-color: var(--ui-switch-track-background--hover); } :where(input[role="switch"]:checked) { background-color: var(--ui-switch-track-background--checked); } :where(input[role="switch"]:hover:checked) { background-color: var(--ui-switch-track-background--checked-hover); } :where(input[role="switch"]:focus-visible) { --_ring-offset-width: var(--ring-offset-width); --_ring-width: var(--ring-width); outline-color: transparent; }}