Toggle
A two-state on/off button for a formatting mark, view option, or setting.
@garn/ui/toggleDefault
An icon + text toggle.
Appearance
Fill treatment: `ghost` is borderless; `outline` adds a bordered, raised surface.
Ghost
ghostOutline
outlineSizing
Height rung (--garn-control-h-*); min-width matches so icon-only toggles stay square.
smmdlgSpacing
The same control rendered across garn's density modes — compact, default, and spacious.
States
Interactive states, toggled live.
<Toggle>B</Toggle>State
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add toggle- Registry
toggle- Source
- toggle.tsx
- Deps
@radix-ui/react-toggleclass-variance-authority- Registry deps
utils
Import
import { Toggle } from "@garn/ui/toggle";Props
Generated from the component's TypeScript types.
appearance- Description
- No description.
- Type
"ghost" | "outline"- Default
ghost
size- Description
- No description.
- Type
"lg" | "md" | "sm"- Default
md
Plus 294 inherited native <button> attributes.
Styling
Target these data-slots and remap these tokens to restyle without forking the component.
[data-slot="toggle"]| Property | Token | Tier |
|---|---|---|
height | --garn-control-h-md | semantic · air |
ring | --garn-ringbrandable | semantic |
Overview
Toggle is a single two-state (on/off) button — a pressed/unpressed control for a formatting mark (bold), a view option, or a toolbar setting. It looks like a button but holds state via aria-pressed, with appearance (ghost / outline) and size. Use it for a binary control inside a toolbar; for a labelled settings switch use Switch, for a checkbox-style boolean in a form use Checkbox, and for a set of related toggles use ToggleGroup.
Guidelines
Use Toggle for a button that stays pressed. It's the right control for toolbar-style on/off (bold, mute) where the affordance is a button, not a form field; a standalone setting reads better as a Switch. Make the pressed state visually distinct from hover.
Label icon-only toggles and convey state beyond color. An icon toggle needs an aria-label, and the on/off difference must be visible without relying on color alone (fill, outline, the icon itself). The aria-pressed state is what AT announces — keep it accurate.
Reach for ToggleGroup when toggles relate. Several alignment or formatting toggles belong in a ToggleGroup so single-choice/multi-select semantics and shared appearance/size are modeled — don't hand-assemble loose toggles for that.
Best practices
- Use
pressed/onPressedChange(controlled) ordefaultPressed(uncontrolled). - Give icon-only toggles an aria-label.
- Reserve it for state you can toggle, not a fire-once action.
- Don't use a Toggle for a non-stateful action — that's a Button.
- Don't ship an icon-only Toggle with no accessible name.
- Don't use a lone Toggle where a labeled Switch communicates the setting better.
Content guidelines
- For text toggles, use a short verb or noun for the thing toggled (“Bold”, “Grid”).
- For icon-only toggles, pass an aria-label that names what's toggled (“Bold”).
- Keep related toggles' labels parallel so a toolbar reads consistently.
Accessibility
- Role
button- ARIA APG
- button
- Focus
- 1px ring in --garn-ring on :focus-visible.
Enforceable contracts
Give an icon-only toggle an accessible name (aria-label).
when the toggle is icon-only (no text child) and has no aria-label / aria-labelledby / title
Keyboard
| EnterSpace | Toggle the pressed state. |
- • Radix sets aria-pressed to reflect the on/off state.
- • An icon-only toggle has no text — give it an aria-label.
- • Forced colors: the on state maps to Highlight / HighlightText.
- Status
- Stable
- Version
v0.3.0- Introduced
v0.1.0
garn doesn't yet record a per-release version history. This reflects the component's current lifecycle state; full changelog entries will appear once the metadata pipeline emits a lifecycle.history field.