Switch is a binary on/off toggle, built on @radix-ui/react-switch for keyboard and ARIA behavior. Use it for settings that take effect immediately (e.g. "enable notifications") — for a choice among several mutually-exclusive options, use RadioGroup or .ods-segmented instead.
Installation
HTML — already included if you loaded the 3 stylesheets — see the Installation guide.
React
npm install @operant/ui
@operant/ui is currently a monorepo workspace package, not yet published to the public npm registry.
Usage
HTML
<button class="ods-switch" role="switch" aria-checked="false" aria-label="Enable notifications">
<span class="ods-switch-thumb"></span>
</button>
React
import { Switch } from "@operant/ui";
export default function Example() {
return <Switch aria-label="Enable notifications" />;
}
Examples
Default (unchecked)
HTML
<button class="ods-switch" role="switch" aria-checked="false" aria-label="Enable notifications">
<span class="ods-switch-thumb"></span>
</button>
React
<Switch aria-label="Enable notifications" />
Checked
[data-state="checked"] fills the track with --color-primary and translates .ods-switch-thumb 16px to the right.
HTML
<button class="ods-switch" role="switch" aria-checked="true" data-state="checked" aria-label="Enable notifications">
<span class="ods-switch-thumb"></span>
</button>
React
<Switch defaultChecked aria-label="Enable notifications" />
Disabled
.ods-switch:disabled / .is-disabled drops opacity to 50% and removes pointer events.
HTML
<button class="ods-switch" role="switch" aria-checked="false" aria-label="Enable notifications" disabled>
<span class="ods-switch-thumb"></span>
</button>
React
<Switch disabled aria-label="Enable notifications" />
With a label
SwitchField pairs the control with visible label text, reusing .ods-checkbox-label (a generic label+control row shared across checkbox/radio/switch, despite the name).
HTML
<label class="ods-checkbox-label" for="notif-switch">
<button class="ods-switch" role="switch" aria-checked="false" id="notif-switch">
<span class="ods-switch-thumb"></span>
</button>
Enable email notifications
</label>
React
import { SwitchField } from "@operant/ui";
<SwitchField label="Enable email notifications" />
API Reference
<Switch>
Wraps Radix SwitchPrimitive.Root + Thumb. Type is React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> — i.e. all native Radix Switch props are accepted directly.
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Controlled checked state. |
defaultChecked | boolean | false | Uncontrolled initial state. |
onCheckedChange | (checked: boolean) => void | — | Called when the state changes. |
disabled | boolean | false | Disables the control. |
required | boolean | false | Marks the field required in a form. |
name | string | — | Form field name. |
className | string | — | Merged with .ods-switch. |
<SwitchField>
Convenience wrapper pairing Switch with a label, wired via id/htmlFor (auto-generated with useId() if id is omitted).
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Required. Label content rendered next to the control. |
| ...rest | SwitchProps | — | All Switch props. |
HTML-side classes: .ods-switch (34×20px pill track; [data-state="checked"] fills it with --color-primary), .ods-switch-thumb (16px circle, translates from 2px to 16px on check), .ods-checkbox-label (shared label+control row).
Accessibility
- Radix renders
role="switch"witharia-checkedkept in sync automatically — screen readers announce it as a toggle, not a checkbox. - Fully keyboard operable: focusable via
Tab, toggled viaSpace. - Focus is visible via
:focus-visible→--shadow-focus; disabled switches are skipped in the tab order. - A
Switchwith no visible label (icon-only or bare) must carry anaria-labeldescribing what it toggles — see the Default/Checked/Disabled examples above, all of which set one. - State changes are silent (no
role="status"announcement) since thearia-checkedchange on the switch element itself is what assistive tech reports — no additional live region is needed for a simple toggle.