Gesso
Actions

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.

PropTypeDefaultWhat 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"> with aria-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-visible only, in the contrast ink rather than the brand color.
  • On a touch screen it is at least 44px tall at every size.
  • disabled sets the native disabled attribute, 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-fill behind it and the toggle has no forced-colors style of its own. aria-pressed still 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

On this page