Monet
@etched/monet 0.0.1

Toggle group

Ported

Toggle buttons that share one state: at most one pressed, or any number with multiple. All can be off. Arrow keys move between them. Use it for view state, such as text formatting or quick filters on a list. For a form value with exactly one choice, use the segmented RadioGroup. Give the group an aria-label, and each toggle a value.

import { TextBIcon, TextItalicIcon, TextUnderlineIcon } from "@phosphor-icons/react";
import { Toggle } from "@etched/monet/toggle";
import { ToggleGroup } from "@etched/monet/toggle-group";

export function Preview() {
  return (
    <ToggleGroup multiple aria-label="Text style" defaultValue={["bold"]}>
      <Toggle value="bold" shape="square" aria-label="Bold" icon={<TextBIcon />} />
      <Toggle value="italic" shape="square" aria-label="Italic" icon={<TextItalicIcon />} />
      <Toggle value="underline" shape="square" aria-label="Underline" icon={<TextUnderlineIcon />} />
    </ToggleGroup>
  );
}

Installation

import { ToggleGroup } from "@etched/monet/toggle-group";

Examples

Several pressed

multiple: each toggle turns on and off by itself.

import { TextBIcon, TextItalicIcon, TextUnderlineIcon } from "@phosphor-icons/react";
import { Toggle } from "@etched/monet/toggle";
import { ToggleGroup } from "@etched/monet/toggle-group";

export function SeveralPressed() {
  return (
    <ToggleGroup multiple aria-label="Text style" defaultValue={["bold", "underline"]}>
      <Toggle value="bold" shape="square" aria-label="Bold" icon={<TextBIcon />} />
      <Toggle value="italic" shape="square" aria-label="Italic" icon={<TextItalicIcon />} />
      <Toggle value="underline" shape="square" aria-label="Underline" icon={<TextUnderlineIcon />} />
    </ToggleGroup>
  );
}

One at most

Without multiple, pressing a toggle releases the others. Pressing the pressed one releases it, so none can be on.

import { TextAlignCenterIcon, TextAlignLeftIcon, TextAlignRightIcon } from "@phosphor-icons/react";
import { Toggle } from "@etched/monet/toggle";
import { ToggleGroup } from "@etched/monet/toggle-group";

export function OneAtMost() {
  return (
    <ToggleGroup aria-label="Text alignment" defaultValue={["left"]}>
      <Toggle value="left" shape="square" aria-label="Align left" icon={<TextAlignLeftIcon />} />
      <Toggle value="center" shape="square" aria-label="Align center" icon={<TextAlignCenterIcon />} />
      <Toggle value="right" shape="square" aria-label="Align right" icon={<TextAlignRightIcon />} />
    </ToggleGroup>
  );
}

Quick filters

Text toggles that narrow a list. value and onValueChange hold the pressed values.

Showing unread
import { useState } from "react";
import { Toggle } from "@etched/monet/toggle";
import { ToggleGroup } from "@etched/monet/toggle-group";

const filters = new Map([
  ["unread", "Unread"],
  ["flagged", "Flagged"],
  ["mine", "Mine"],
]);

export function QuickFilters() {
  const [on, setOn] = useState<string[]>(["unread"]);

  return (
    <div className="row">
      <ToggleGroup multiple size="sm" aria-label="Filter messages" value={on} onValueChange={setOn}>
        {[...filters].map(([value, label]) => (
          <Toggle key={value} value={value}>
            {label}
          </Toggle>
        ))}
      </ToggleGroup>
      <span>{on.length ? `Showing ${on.map((value) => filters.get(value)).join(", ").toLowerCase()}` : "Showing all"}</span>
    </div>
  );
}

Toggle group or segmented radio?

A toggle group holds view state: toggles that can all be off, or several on. A segmented RadioGroup is a form value: exactly one choice, always one chosen, sent with the form. Here, the range is one value; the columns are view state.

Range
import { RadioGroup, RadioItem } from "@etched/monet/radio";
import { Toggle } from "@etched/monet/toggle";
import { ToggleGroup } from "@etched/monet/toggle-group";

export function ToggleGroupOrSegmentedRadio() {
  return (
    <div className="row" style={{ alignItems: "end", gap: "var(--et-space-6)" }}>
      <RadioGroup legend="Range" appearance="segmented" defaultValue="7d">
        <RadioItem value="24h" label="24h" />
        <RadioItem value="7d" label="7d" />
        <RadioItem value="30d" label="30d" />
      </RadioGroup>
      <ToggleGroup multiple aria-label="Columns" defaultValue={["owner", "site"]}>
        <Toggle value="owner">Owner</Toggle>
        <Toggle value="site">Site</Toggle>
        <Toggle value="started">Started</Toggle>
      </ToggleGroup>
    </div>
  );
}

Accessibility

  • The group is one Tab stop. ArrowLeft/ArrowRight move between toggles and wrap, Home/End go to the ends, and Space or Enter presses.
  • A group named by its aria-label. Each toggle is a button with aria-pressed, named by its text or aria-label.
  • Without multiple, pressing one releases the others. Pressing the pressed one releases it, so none is on.
  • Give the group an aria-label and each toggle a value. For a form value with exactly one choice, use a segmented RadioGroup.

API reference

PropTypeDefaultDescription
size"sm" | "base""base"Height of the toggles. In a Toolbar, the toolbar's.
classNamestring–Class for layout.