Photon Design System

Toggle Group

Groups related Toggle buttons into one control with shared roving-focus keyboard navigation — text alignment, view mode, formatting clusters. Built on Base UI ToggleGroup.

Import

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

Basic

Single-select by default — one item pressed at a time; pressing another releases the first. Each Toggle carries a value; track selection with value / onValueChange (an array) or defaultValue.

import {
  IconAlignLeft,
  IconAlignCenter,
  IconAlignRight,
} from "@tabler/icons-react";

<ToggleGroup defaultValue={["left"]}>
  <Toggle value="left" aria-label="Align left">
    <IconAlignLeft />
  </Toggle>
  <Toggle value="center" aria-label="Align center">
    <IconAlignCenter />
  </Toggle>
  <Toggle value="right" aria-label="Align right">
    <IconAlignRight />
  </Toggle>
</ToggleGroup>;

Multiple

Set multiple when several items can be pressed at once — a formatting cluster where Bold, Italic, and Underline stack. The value is still an array; it simply holds every pressed item.

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

<ToggleGroup multiple defaultValue={["bold"]}>
  <Toggle value="bold" aria-label="Bold">
    <IconBold />
  </Toggle>
  <Toggle value="italic" aria-label="Italic">
    <IconItalic />
  </Toggle>
  <Toggle value="underline" aria-label="Underline">
    <IconUnderline />
  </Toggle>
</ToggleGroup>;

Controlled

Pass value and onValueChange to own the selection — the callback hands back the pressed-values array, so read value[0] for a single-select group and the whole array when multiple. Use defaultValue instead for the initial value only.

Aligned left
const [value, setValue] = useState<string[]>(["left"]);

<ToggleGroup value={value} onValueChange={setValue}>
  {/* …toggles… */}
</ToggleGroup>;

Orientation

orientation="vertical" stacks the group and swaps the arrow-key axis to Up/Down. It sets data-orientation for styling; lay the items out in a column yourself (here, className="flex-col").

<ToggleGroup
  orientation="vertical"
  defaultValue={["left"]}
  className="flex-col"
>
  {/* …toggles… */}
</ToggleGroup>

Disabled

disabled on the group ignores interaction across every item and dims the whole control; the current selection stays visible.

<ToggleGroup defaultValue={["center"]} disabled>
  {/* …toggles… */}
</ToggleGroup>

Anatomy

  • ToggleGroup — the roving-focus container (role="group"); owns the selected-values array and the multiple / orientation behavior.
  • Toggle — each two-state button, from @photon-ai/pho-ui/components/toggle. Every child carries a value that keys it in the group’s array, and an aria-label since it’s icon-only.

Props

  • value — readonly string[] — controlled pressed values (the counterpart of defaultValue)
  • defaultValue — readonly string[] — uncontrolled initial pressed values
  • onValueChange — (value: string[], details) => void — fired when the pressed set changes
  • multiple — boolean (default false) — allow more than one item pressed at once
  • disabled — boolean (default false) — ignore interaction for the whole group
  • orientation — horizontal · vertical (default horizontal) — arrow-key axis
  • loopFocus — boolean (default true) — wrap arrow-key focus at the ends
  • className — string — merged onto the group <div>

Each child Toggle takes its own value (required) and a size of sm · md · lg (default md) — see the Toggle page.

Accessibility

  • Semantics — the group renders a <div role="group">; each Toggle is a <button> that exposes its state through aria-pressed. Because the toggles here are icon-only, every one needs an aria-label that names its action.
  • Focus — roving: only one toggle is tabbable at a time, so the group reads as one control rather than N buttons. orientation sets both the arrow-key axis and the data-orientation attribute.

Keyboard: the group is a single tab stop (roving tabindex).

  • Tab — move focus into the group, onto the active/first toggle, then out
  • Arrow keys — move focus between toggles (Left/Right when horizontal, Up/Down when vertical); focus wraps at the ends while loopFocus is on
  • Enter / Space — toggle the focused item

Best practices

  • Reach for a toggle group when the options are mutually related and one selection is the norm (alignment, view mode). For independent on/off settings, use separate Switches.
  • Set multiple only when several can be active together (e.g. Bold + Italic); otherwise keep the default single-select.
  • Each Toggle is icon-only, so an aria-label per item is required — name the action (Align left), not the glyph.
  • Keep the item count small and the icons unambiguous; a toggle group is a glanceable control, not a menu.