Pho Design System

Toggle

A two-state button that stays pressed — the canonical Bold / Italic formatting control. Built on Base UI Toggle, which manages the pressed state and aria-pressed. For a set of related toggles (alignment, view mode), use ToggleGroup; for an on/off setting, use Switch.

Import

import { Toggle } from "@photon-ai/pho-ui/components/toggle";

Basic

Each toggle is icon-only and needs an aria-label. Use defaultPressed for an initial on state. The pressed toggle renders as the inverted chip (the site’s Tab-active fill); disabled dims to 50%.

import { IconBold, IconItalic } from "@tabler/icons-react";

<Toggle aria-label="Bold">
  <IconBold weight="bold" />
</Toggle>
<Toggle aria-label="Italic" defaultPressed>
  <IconItalic />
</Toggle>

Sizes

Three square sizes — sm, md, lg — sized to sit in a toolbar next to inputs. The default is md.

<Toggle size="sm" aria-label="Bold"><IconBold weight="bold" /></Toggle>
<Toggle size="md" aria-label="Bold"><IconBold weight="bold" /></Toggle>
<Toggle size="lg" aria-label="Bold"><IconBold weight="bold" /></Toggle>

Controlled

Pass pressed and onPressedChange to own the state — read it back to swap the icon, persist the choice, or drive other UI off it.

Not saved
import { IconStar } from "@tabler/icons-react";

const [pressed, setPressed] = useState(false);

<Toggle
  aria-label="Add to favorites"
  pressed={pressed}
  onPressedChange={setPressed}
>
  <IconStar weight={pressed ? "fill" : "regular"} />
</Toggle>;

Props

Beyond these, Toggle forwards native <button> attributes and className.

Accessibility

Toggle renders a native <button> with aria-pressed reflecting the state (mirrored as data-pressed for styling, data-disabled when off-limits). Because it carries only an icon, an aria-label naming the action is required — without it the control is unlabelled to screen readers.

Keyboard:

Focus is shown with the pho-brand focus-visible ring. State is announced through aria-pressed, so no live region is needed.

Best practices