Buttons and controls
Switch
Turn a setting on or off.
import { Switch } from "macbetter/switch"
export default function Example() {
return (
<>
<Switch aria-label="Wi-Fi" defaultChecked />
<Switch aria-label="Bluetooth" />
</>
)
}When to use it
Use a switch for a setting that takes effect the moment it changes, like turning on Wi-Fi or Low Power Mode. If the change only applies when someone presses Save or OK, use a Checkbox instead.
- Name what the switch controls, not its state: "Bluetooth", not "Turn Bluetooth on".
- On macOS, prefer a checkbox for most settings in a window. Use a switch for a setting that controls a whole group or a major feature, as System Settings does.
- On iOS and iPadOS, a switch is the standard way to show a binary setting in a list row.
Usage
import { Switch } from "macbetter/switch"In a settings row
Put the switch in a Field with a FieldLabel, and the label names it and toggles it when clicked. A FieldDescription is read with it.
import { Field, FieldDescription, FieldLabel } from "macbetter/field"
import { Switch } from "macbetter/switch"
// The label and description on the leading side, the switch on the trailing edge.
const row = { display: "grid", gridTemplateColumns: "1fr auto", alignItems: "center", gap: "2px 16px" }
const title = { gridColumn: 1, padding: 0, color: "var(--mb-label)", font: "var(--mb-text-body)" }
export default function Example() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 18, width: "100%", maxWidth: 420 }}>
<Field style={row}>
<FieldLabel style={title}>Low Power Mode</FieldLabel>
<Switch style={{ gridColumn: 2, gridRow: "1 / span 2" }} />
<FieldDescription style={{ gridColumn: 1, padding: 0 }}>
Reduces background activity until you charge your device.
</FieldDescription>
</Field>
<Field style={row}>
<FieldLabel style={title}>Show Battery Percentage</FieldLabel>
<Switch style={{ gridColumn: 2 }} defaultChecked />
</Field>
</div>
)
}Sizes
size="sm" is macOS's mini switch, for dense inspectors and tables. On touch, it's a smaller switch for compact rows.
import { Switch } from "macbetter/switch"
export default function Example() {
return (
<>
<Switch size="sm" aria-label="Snap to Grid" defaultChecked />
<Switch size="md" aria-label="Show Rulers" defaultChecked />
</>
)
}Disabled
A disabled switch keeps its state visible but can't be focused or changed. Say why nearby, when it isn't obvious.
import { Switch } from "macbetter/switch"
export default function Example() {
return (
<>
<Switch aria-label="iCloud Drive" disabled defaultChecked />
<Switch aria-label="Desktop and Documents Folders" disabled />
</>
)
}Platform behavior
- macOS. A 38 × 22 point capsule. The thumb stretches toward where it's going while it's held, then springs across.
- iPadOS and iOS. The 51 × 31 point UISwitch with a wider stretch, so it feels right under a thumb.
- Every platform. On is green (
--mb-green), as it is everywhere in Apple's platforms, whatever the theme's accent. The hit area is extended to the platform minimum.
Accessibility
- Renders a button with the
switchrole andaria-checked, plus a hidden native checkbox for forms. - Space toggles it; Enter submits its form and never toggles it.
- Name it with a
FieldLabeloraria-label. The label stays the same in both states. - The on state is a fill, not only a color: the thumb also moves to the trailing side.
API
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "md" | md is the standard switch; sm is the mini switch. |
checked | boolean | — | Whether it's on. Pass it to control the switch. |
defaultChecked | boolean | false | Whether it's on at first, when uncontrolled. |
onCheckedChange | (checked, details) => void | — | Runs when it wants to change. |
disabled | boolean | false | Can't be focused or changed, and isn't submitted. |
readOnly | boolean | false | Focusable and submitted, but doesn't change. |
name | string | — | The name it submits under. |
children | ReactNode | — | Replaces the built-in thumb, if you need a custom one. |
SwitchThumb
The thumb Switch renders for you. Render it yourself, as the switch's child, only to put something inside it or give it your own class. It takes the props of ui-sdk's SwitchThumb.
Data attributes
| Attribute | Values |
|---|---|
data-size | sm, md |
data-checked / data-unchecked | Present for the current state |
data-disabled, data-readonly, data-invalid | Present in those states |
CSS variables
| Variable | macOS | iOS | What it sets |
|---|---|---|---|
--mb-switch-width | 38px | 51px | Track width |
--mb-switch-height | 22px | 31px | Track height |
--mb-switch-width-sm | 26px | 42px | Small track width |
--mb-switch-height-sm | 15px | 26px | Small track height |
--mb-switch-thumb-stretch | 3px | 6px | How far the thumb stretches while held |
--mb-switch-on | --mb-green | --mb-green | The on color, set on the switch |