Switch
A Base UI Switch for a setting that takes effect the moment it flips, with a label and an "On" / "Off" state word.
import { Switch } from '@fairgarden-private/design/components/Switch'
Label the setting, not the action. In a form that is submitted later, use a Checkbox instead, and never use a switch for a choice that isn't on or off. Off → on changes the thumb's position and shape (ring → disc), the edge weight and the fill, so the state never rests on fill alone.
States
States
kind="row" (default) puts the label left and the switch right, with a hairline rule under each row; kind="inline" puts the switch first. Hover gives an off track a soft --primary3 fill where soft fills apply, lightens an on track to --role-select-hover and underlines the label; the edge and thumb never change. Disabled draws a dotted track and a muted thumb ring. While a change saves remotely, pass aria-busy: the state word reads "Saving…" and the switch is inert; on failure revert checked and explain in a toast.
'use client'
import * as React from 'react'
import { Switch } from '@fairgarden-private/design/components/Switch'
import styles from './states.module.css'
/**
* A settings list of rows (on, off, disabled, and a remote save that shows
* "Saving…"), then an inline switch.
*/
export function SwitchStates() {
const [alerts, setAlerts] = React.useState(true)
const [saving, setSaving] = React.useState(false)
const save = (next: boolean) => {
setSaving(true)
window.setTimeout(() => {
setAlerts(next)
setSaving(false)
}, 1200)
}
return (
<div className={styles.stack}>
<div>
<Switch
checked={alerts}
onCheckedChange={save}
aria-busy={saving}
description="Applies at once; saving takes a moment."
>
Email Alerts
</Switch>
<Switch>Trail Closures</Switch>
<Switch disabled>Text Messages</Switch>
<Switch disabled defaultChecked>
Member Mail
</Switch>
</div>
<Switch kind="inline" defaultChecked>
Show Elevation
</Switch>
</div>
)
}
Primary and secondary
Primary and secondary
secondary drives the on track and thumb through --role-select; primary drives the off track, the thumb ring, the label and the state word. Neither is defaulted.
import { Switch } from '@fairgarden-private/design/components/Switch'
import styles from './color.module.css'
/**
* `secondary` drives the on track and thumb through --role-select;
* `primary` drives the off track, thumb ring, label and state word.
*/
export function SwitchColor() {
return (
<div className={styles.grid}>
<Switch kind="inline" defaultChecked>
Scope Colors
</Switch>
<Switch kind="inline" defaultChecked secondary="indigo">
Secondary Indigo
</Switch>
<Switch kind="inline" primary="plum">
Primary Plum
</Switch>
</div>
)
}
On grounds
On paper and forest
On is green on paper and the inverse pair on forest and the saturated grounds. In print the graphic is hidden and each row prints as "Label: On".
import { Switch } from '@fairgarden-private/design/components/Switch'
import { PresetGround } from '@/components/PresetGround'
import styles from './grounds.module.css'
const presets = ['paper', 'forest'] as const
/** On is green on paper and the inverse pair on forest; position, thumb shape and edge weight carry it on both. */
export function SwitchGrounds() {
return (
<div className={styles.row}>
{presets.map((preset) => (
<PresetGround key={preset} preset={preset} className={styles.face}>
<span className={styles.name}>{preset}</span>
<div>
<Switch defaultChecked>Map Labels</Switch>
<Switch>Contour Lines</Switch>
<Switch disabled>Satellite View</Switch>
</div>
</PresetGround>
))}
</div>
)
}
API Reference
A Base UI Switch for a setting that takes effect the moment it flips.
Off → on changes position, thumb shape (ring → disc), edge weight and
fill, so state is never fill alone [D15]. While the change is saving,
pass aria-busy: the state word reads “Saving…” and the switch is
inert; on failure revert checked and explain in a toast (§10.17).
| Prop | Type | Description |
|---|---|---|
busyLabel | | The state word while |
description | | Optional helper text under the label, in sentence case. |
kind | |
|
primary | | Primary Radix scale: the off track and thumb, label and state word. Never defaulted; omitted, it inherits the scope [D133]. |
secondary | | Secondary Radix scale: the on track’s fill and edge and the on thumb, through --role-select. Omitted, it inherits the scope. |
stateLabels | | The state words; default “On” / “Off”. Set in |
children | | The label: name the setting, not the action, in title case [D160]. |
className | | Class for the row (the |
SwitchProps
Props for Switch: Base UI Switch.Root props (on the track) plus the
kind, color axes and the row’s content. className goes on the row.
type SwitchProps = {
/**
* `row` (default): label left, switch right, the row a hit target with a
* hairline rule. `inline`: switch, state word, then label.
*/
kind?: 'row' | 'inline' | null;
/**
* Primary Radix scale: the off track and thumb, label and state word.
* Never defaulted; omitted, it inherits the scope [D133].
*/
primary?:
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null;
/**
* Secondary Radix scale: the on track's fill and edge and the on thumb,
* through --role-select. Omitted, it inherits the scope.
*/
secondary?:
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null;
/** Class for the row (the `base` part). */
className?: string;
/** The label: name the setting, not the action, in title case [D160]. */
children?: React.ReactNode;
/** Optional helper text under the label, in sentence case. */
description?: React.ReactNode;
/** The state words; default "On" / "Off". Set in `type-label` caps. */
stateLabels?: SwitchStateLabels;
/** The state word while `aria-busy` is set; default "Saving…". */
busyLabel?: string;
}switchRoot
type switchRoot = switchRootSwitchStateLabels
The state words shown beside the track.
type SwitchStateLabels = {
/** Shown while on. Default "On". */
on: string;
/** Shown while off. Default "Off". */
off: string;
}Specification: DESIGN-SYSTEM.md §10.9 (switch) and §10.1 (the selected fill).