Switch
A switch flips a setting that takes effect immediately; use a
Checkbox for something that is submitted later. It is
Headless UI's Switch (a <button role="switch"> with a hidden input for
forms) drawn as a track and thumb. With label or description it wraps
itself in a Headless UI Field, so clicking the label toggles it and the
description lands in aria-describedby.
import Switch from '@zuilib/components/switch'
Basic
checked + onChange for controlled, defaultChecked for uncontrolled.
onChange receives the boolean, not an event.
Sizes and label position
Track, thumb and thumb travel scale with --spacing (h-4 w-7, h-5 w-9,
h-6 w-11); the text beside it is text-sm for sm and md,
text-base for lg. labelPosition="start" puts the text first, which
suits a settings list where the toggles line up on the right.
Invalid
invalid sets aria-invalid, paints the track border --destructive and
turns the keyboard ring destructive.
In a FormItem
Without label / description only the track renders, so it picks up the
enclosing FormItem's Label, Description and Message through the
Headless UI Field. invalid defaults to the item's invalid, and
disabled (left undefined) inherits from the Field / Fieldset.
Props
SwitchProps extends Headless UI SwitchProps (minus as, children)
with:
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Controlled state; use defaultChecked for uncontrolled |
defaultChecked | boolean | false | |
onChange | (checked: boolean) => void | — | |
label | ReactNode | — | Visible label; wraps the control in a Field so clicking it toggles |
description | ReactNode | — | Helper text wired through aria-describedby |
labelPosition | 'end' | 'start' | 'end' | Where the label and description sit relative to the track |
size | 'sm' | 'md' | 'lg' | 'md' | |
invalid | boolean | FormItem invalid | aria-invalid, data-invalid, destructive border and ring |
disabled | boolean | Field / FormItem disabled | Leave unset to inherit; an explicit false overrides the context |
name | string | — | Native form participation via a hidden input |
value | string | 'on' | Submitted value when on |
id | string | — | Replaces the Field-generated id; the label's htmlFor follows it |
className | string | — | Merged last onto the track (the element ref points at) |
Slots
| Slot | Element | Notes |
|---|---|---|
[data-slot="switch"] | button | The track, role="switch". Headless UI sets data-checked, data-hover, data-focus, data-disabled; ZUI adds data-invalid |
[data-slot="switch-thumb"] | span | The knob; aria-hidden, slides on data-checked |
[data-slot="switch-field"] | div | Present with label / description: the Headless UI Field |
[data-slot="switch-text"] | div | Column holding label and description |
[data-slot="switch-label"] | label | — |
[data-slot="switch-description"] | p | — |
/* Consumer CSS: a success-coloured "on" track */
[data-slot="switch"][data-checked] {
background-color: var(--success);
}
Tokens
| Token | Used for |
|---|---|
--spacing | Track, thumb and travel per size |
--muted, --muted-foreground | Off track and its hover |
--primary | On track |
--background, --shadow-medium | The thumb |
--destructive | Invalid border and ring |
--ring | Keyboard focus ring |
--duration-normal | Track colour and thumb travel |
--text-sm, --text-base | Label and description |
Accessibility
- The track is a
<button role="switch">witharia-checked; a hidden input carriesname/valueto the form. - Space and Enter toggle; Tab moves through switches in document order.
- With
labelthe<label>is associated to the track through the Field, so clicking the label toggles it. Pass the sameidon the control when you set one; the label'shtmlForfollows it. descriptionand aFormItem'sDescription/Messageare joined intoaria-describedby.invalidsetsaria-invalid; keyboard focus shows the shared--ringring (focus-visibleonly), destructive when invalid.disabledsetsdata-disabledandaria-disabled, dims the track and label and showscursor-not-allowed.- Thumb travel and track colour transitions are off under
prefers-reduced-motion.
Related
- Checkbox: the same anatomy for a value submitted later.
- Radio group: one of many.
- FormItem, Label, Message: the field wiring.