Switch
An on/off control whose change takes effect immediately, with a visible label and an optional description.
Basic usage
Install the package:
pnpm add @design-schema/react @design-schema/tokens Import the stylesheet once for the app, then the component:
import '@design-schema/tokens/calm-precise/css';
import '@design-schema/react/index.css';
import { Switch } from '@design-schema/react'; Examples
import { Switch } from '@design-schema/react';
<Switch label="Email notifications" />Props
7 rows| Prop | Type | Default | Description |
|---|---|---|---|
| label required | string | — | Visible label naming the thing being turned on or off. Also the accessible name. Accessibility: Associated with the control (label/for on web, accessibilityLabel on native). |
| name | string | — | Optional field name. When inside a Form the state is collected as a boolean on every platform; most switches are not in forms. Without `name` a Switch inside a Form does not register (it contributes no key) and its id comes from the platform id generator (useId on web), not the Form's idBase. A Switch never validates and never appears in an error summary. |
| checked | boolean | — | Controlled state (web, RN, SwiftUI): a controlled switch shows a new state only once this prop changes. Omit for an uncontrolled control. Lit has no controlled mode: the `checked` property is the live state like a native input (it starts from the `checked` attribute, else `defaultChecked`, and every toggle updates it). |
| defaultChecked | boolean | false | Initial state for an uncontrolled control. |
| disabled | boolean | false | Cannot be toggled. Stays visible, readable and focusable on web, Lit and SwiftUI; a disabled native React Native Switch is not focusable (platform limit, see platforms.rn.notes). A disabled switch contributes no key to the Form's values on any platform (the Form's disabled-field rule, as for Checkbox): on web and RN it stays registered and reports itself through the registration's `isDisabled()`, which the Form already uses to skip a field — one mechanism for the rule, the same one Checkbox uses, rather than unregistering and re-registering as the prop flips; on Lit it calls setFormValue(null) while disabled or form-disabled, so neither a native <form> nor ds-form collects it. |
| description | string | — | Persistent helper text below the label explaining the effect. Accessibility: Linked with aria-describedby / accessibilityHint. |
| labelPosition | start | end | start | Where the label sits relative to the track. `start` (label, then switch at the row end) is the settings-list convention; `end` matches Checkbox. |
Events
1 row| Event | Type | Description |
|---|---|---|
| onChange | onChange / change / onValueChange | Fired when the user changes the state, with the new boolean. The change is already in effect; there is nothing to submit. A controlled prop change fires nothing, a press that asks for the current value (next equals checked) fires nothing, and nothing fires on mount. The next-equals-checked rule needs a guard only where a platform can report the current value (RN valueChange, SwiftUI); on web and Lit every native toggle flips the value, so no guard is written there. |
Accessibility
Decisions this component leaves to you
Supply an accessible name. This component has no text of its own to fall back on, so without one a screen reader announces its role and nothing else.
Supply the label. It is associated with the control for you, but the words are yours — a placeholder is not a label.
Required props that carry accessibility meaning: label. Each is described in the props table above.
Implicit ARIA role: switch.
Anatomy
What generation guarantees
Accessible name — Every instance carries a programmatic name. Where the component has no text of its own to use, the name is a prop the consumer supplies.
Label association — The label is programmatically associated with the control, not merely placed beside it.
Visible focus — Keyboard focus is always drawn, from the theme’s focus tokens, and is never removed.
Keyboard operable — Everything the pointer can do here, the keyboard can do — see the keyboard section.
Target size — Interactive targets are at least 24×24 CSS pixels (WCAG 2.2 Target Size, minimum).
Contrast — Text and meaningful non-text contrast meets WCAG AA; the pairs are listed below.
Reduced motion — Under `prefers-reduced-motion: reduce` the transitions are dropped rather than shortened, and the component still reaches every state.
Under `prefers-reduced-motion: reduce` the transitions are dropped rather than shortened, and the component still reaches every state.