Instant on-or-off with optimistic feedback.
| Prop / event | Type | Notes |
|---|---|---|
| checked | boolean | Controlled on/off state |
| onChange | (next) => void | Fires on click, Enter and Space |
| size | l · m · s | Track 48 / 40 / 32px wide |
| label / description | string | Row is clickable; description is optional |
| disabled | boolean | Blocks interaction, opacity 0.42 |
| role="switch" | a11y | aria-checked reflects state; Tab-focusable |
A toggle changes state immediately and never shows a spinner on itself. If the change needs the network, apply it optimistically and reconcile in the background — the Optimistic tab in the demo above does exactly that.
Use a toggle for an instant, standalone on/off with no confirmation needed.
Never toggle a destructive or irreversible action. That belongs to a button and a confirm.
Label the state, not the verb. "Auto-save" reads clearly whether it is on or off.
Don't phrase the label as a question or an action. A toggle is a state, not a prompt.
Controls align to the right edge, labels to the left. One row per setting.
Small size pairs with a short mono label for compact, dense surfaces.
A description sits under the label when the choice needs explaining.
| Token | Value | Role |
|---|---|---|
| --tg-track-l/m/s | 48×28 / 40×24 / 32×19 | Track size per scale |
| --tg-knob-l/m/s | 22 / 18 / 14px | Sliding knob diameter |
| --tg-off-track | #26262B | Off track fill |
| --tg-off-knob | #9AA0A8 | Off knob |
| --tg-on | #FF7A00 | On track & border (the one light) |
| --tg-on-knob | #FFFFFF | On knob |
| --tg-ease | cubic-bezier(.34,1.56,.64,1) | Knob spring, 0.22s |
| --tg-focus | rgba(255,122,0,.35) | 3px focus-visible ring |
<button class="o-toggle o-toggle--m" role="switch" aria-checked="true"></button> <label class="o-toggle-row"> <button class="o-toggle o-toggle--m is-on" role="switch" aria-checked="true"></button> <span>Email notifications</span> </label>