UNPKG

@loke/ui

Version:
143 lines (101 loc) 3.71 kB
--- name: switch type: core domain: forms requires: [loke-ui] description: > Switch + SwitchThumb for binary on/off toggles. role=switch semantics, no indeterminate state. Hidden native checkbox input for form participation. Label required for accessible name. Use Checkbox for tri-state or multi-select scenarios. --- # Switch `@loke/ui/switch` — toggle switch built on `Primitive.button` with `role="switch"`. A hidden `<input type="checkbox">` handles form participation. No indeterminate state. **Exports:** `Switch`, `SwitchThumb`, `createSwitchScope` ## Setup ```tsx import { Switch, SwitchThumb } from "@loke/ui/switch"; import { Label } from "@loke/ui/label"; function NotificationToggle() { return ( <div style={{ display: "flex", alignItems: "center", gap: 8 }}> <Label htmlFor="notifications">Email notifications</Label> <Switch id="notifications" name="notifications" defaultChecked> <SwitchThumb /> </Switch> </div> ); } ``` `data-state` on `Switch` and `SwitchThumb`: `"checked"` | `"unchecked"` ## Core Patterns ### Controlled state ```tsx const [enabled, setEnabled] = useState(false); <Switch checked={enabled} onCheckedChange={setEnabled} name="darkMode" > <SwitchThumb /> </Switch> ``` ### Form participation Inside a `<form>`, a hidden `<input type="checkbox">` is rendered automatically. Use `name` and `value` props to control the submitted field. ```tsx <form onSubmit={handleSubmit}> <Switch name="marketing" value="yes" defaultChecked={false}> <SwitchThumb /> </Switch> <button type="submit">Save</button> </form> ``` For a Switch outside its form element, pass the `form` prop with the form's `id`: ```tsx <Switch name="analytics" form="settings-form" defaultChecked> <SwitchThumb /> </Switch> ``` ## Common Mistakes ### 1. Using Switch when Checkbox semantics are needed Switch uses `role="switch"` — a binary on/off control. It has no indeterminate state and no multi-select semantics. Screen readers announce it as "on" or "off". **Wrong:** Using Switch for selecting items in a list, or for a tri-state header row in a table. **Correct:** Use `Checkbox` with `checked="indeterminate"` for tri-state. Use `Checkbox` for selecting items from a set. Use `Switch` only for settings-style on/off toggles. Source: `src/components/switch/switch.tsx` — `role="switch"`, no indeterminate branch ### 2. Forgetting SwitchThumb `Switch` renders the track button only. `SwitchThumb` is the sliding indicator element that moves between checked and unchecked positions. Without it the switch has no visible affordance. **Wrong:** ```tsx <Switch name="active" /> ``` **Correct:** ```tsx <Switch name="active"> <SwitchThumb /> </Switch> ``` `SwitchThumb` reads checked state from context — it must be a child of `Switch`. Source: `src/components/switch/switch.tsx` — `SwitchThumb` uses `useSwitchContext` ### 3. Missing label — no accessible name Switch renders as `<button role="switch">`. Without an associated Label, screen readers announce the control without context. **Wrong:** ```tsx <Switch name="wifi"><SwitchThumb /></Switch> <span>Wi-Fi</span> ``` **Correct:** ```tsx <Label htmlFor="wifi">Wi-Fi</Label> <Switch id="wifi" name="wifi"><SwitchThumb /></Switch> ``` Or wrap: ```tsx <Label> Wi-Fi <Switch name="wifi"><SwitchThumb /></Switch> </Label> ``` Source: `src/components/switch/switch.tsx` — renders `Primitive.button` ## Cross-references - **Label** (`@loke/ui/label`) — accessible labelling for Switch - **Checkbox** (`@loke/ui/checkbox`) — tri-state and multi-select scenarios - **Choosing the Right Component**Switch vs Checkbox decision guide