Toggle
Pressed / unpressed control. Building block for Toggle group.
Toggle is a button that stays pressed until it is pressed again, for a state like wrapped
lines, a visible panel, or bold text. It fills with bg-fill while it is on, and its corners
follow the look's rounded-control. It is also the building block of
Toggle group, which joins several toggles into one
control.
This line wraps onto the next one while the toggle is on, and runs off the edge when it is off.
Usage
import { TextB } from '@phosphor-icons/react';
import { Toggle } from '@/components/ui/toggle';
<Toggle aria-label="Bold" pressed={bold} onPressedChange={setBold}>
<TextB />
</Toggle>Holding the pressed state
Pass pressed and onPressedChange to hold the state yourself, as in the usage above, or
defaultPressed to let the toggle keep it. While it is on, the toggle fills with bg-fill,
turns its text text-fg, and sets data-state="on" for your own styles. Hover paints the
same bg-fill, so an off toggle under the pointer looks like an on one until the pointer
moves away.
Default or outline
default is transparent until it is on, for a toolbar or a row that already frames it.
outline adds a border-border stroke, so the toggle still reads as a control while it is
off, and suits a toggle that stands alone. Both fill with bg-fill when they are on.
Three sizes
sm is 32px tall, default is 36px, and lg is 40px. Each is at least as wide as it is
tall, so a toggle with only an icon is square. On a touch screen every size grows to at least
44px tall. An icon inside is 16px unless you give it a size-* class.
Props
Toggle takes every prop of the Radix Toggle, such as pressed, defaultPressed, and
onPressedChange, and every <button> attribute, plus these.
| Prop | Type | Default | What it does |
|---|---|---|---|
variant | "default" | "outline" | "default" | outline adds a border, so the toggle reads as a control while it is off. |
size | "sm" | "default" | "lg" | "default" | 32, 36, or 40px tall, and at least 44px on a touch screen. |
To style something that is not a Toggle, toggleVariants({ variant, size }) returns the
classes.
Accessibility
- It renders a native
<button type="button">witharia-pressed, so Enter and Space flip it, it never submits a form, and assistive tech reports whether it is pressed. - The accessible name is its text. A toggle with only an icon needs an
aria-label, and it shows no tooltip of its own, so wrap it in a Tooltip when sighted users need the name too. - Focus shows a 2px ring on
:focus-visibleonly, in the contrast ink rather than the brand color. - On a touch screen it is at least 44px tall at every size.
disabledsets the nativedisabledattribute, so the toggle leaves the tab order, dims, and is reported as unavailable.- Under reduced motion the hover and on fills change without a transition.
- In forced colors the on state does not show, because the system replaces the
bg-fillbehind it and the toggle has no forced-colors style of its own.aria-pressedstill reports it, so let the label or icon change with the state too when Windows high contrast users need to see it.
When to use something else
- Toggle group when several toggles belong together, so they share one tab stop and the arrow keys, or when exactly one of them stays on, like List or Board. A row of single toggles cannot hold that choice.
- Switch for a setting with its label beside it, as in a settings list.
- Checkbox for a yes or no answer that a form submits, since a toggle has no form value.
- Button for an action that runs once and does not stay on.
Install
shadcn add @gesso/toggle