InputsSwitch
Switch
The on/off toggle. The label describes the destination state, not the action — 'Reduce motion' is on, not 'Enable reduce motion'.
States
Off, on, disabled. The thumb is --background on a --foreground track when on.
States
Anatomy
- 32 × 18 px track, 9999 px radius (pill)
- 14 × 14 px thumb,
--backgroundcolor, 2 px transition on translate - Off:
--bordertrack. On:--foregroundtrack. - Focus ring is the system default applied to the input
Implementation
switch.tsx
import { forwardRef, type InputHTMLAttributes } from "react"
import { cn } from "@/lib/utils"
interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
label: string
size?: "sm" | "md"
}
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
function Switch({ label, size = "md", className, ...props }, ref) {
return (
<label className={cn("ds-switch", `ds-switch--${size}`, className)}>
<input ref={ref} type="checkbox" role="switch" {...props} />
<span className="ds-switch__track" aria-hidden="true">
<span className="ds-switch__thumb" />
</span>
<span className="ds-switch__label">{label}</span>
</label>
)
},
)Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | None | Required: the label |
checked | boolean | false | On or off |
disabled | boolean | false | Disabled state |
size | "sm" | "md" | "md" | Track size |
When to use a switch, when a checkbox
A switch is for binary state that takes effect immediately. A checkbox is for binary selection that takes effect on submit. "Email me a weekly digest" is a checkbox (saved with the rest of the form). "Reduce motion" is a switch (toggled, and the page re-renders).