Acrylic
Components

Button Group

macOS 26 grouped control — attached buttons in a gray well, a backgroundless ghost group, a selectable segmented control with a raised white pill, or a split group.

The macOS 26 grouped control. A set of buttons sits inside a rounded gray "well"; adjacent buttons are flush and separated by thin hairline dividers. For a selectable group, the active item floats as a raised white pill over the well. Anatomy (well fill, hairline color, segment radius, pill shadow) is lifted from the Apple macOS 26 UI Kit "Segmented Controls" page; every color resolves through the Acrylic theme tokens so the group flips light/dark. Reuses the Acrylic Button for its items.

Installation

npx shadcn add https://acrylic-ui.vercel.app/r/button-group.json

Usage

import { Button } from "@/components/acrylic/button"
import {
  ButtonGroup,
  ButtonGroupSeparator,
  ButtonGroupText,
} from "@/components/acrylic/button-group"

<ButtonGroup>
  <Button variant="neutral">Cut</Button>
  <ButtonGroupSeparator />
  <Button variant="neutral">Copy</Button>
</ButtonGroup>

Examples

attached
with text
Zoom
ghost
toolbar
segmented
labeled
multiple
disabled
both
A only
B only
neither
disabled (selectable)
split

Parts

  • ButtonGroup — the wrapper. Renders a role="group" container and connects its children into the macOS well.
  • ButtonGroupItem — a segment in the single-select segmented control (keyed by value). The sliding white pill marks the selected one.
  • ButtonGroupToggle — a toggle in the multi-select group (type="multiple", keyed by value). When on it tints to the accent; several can be on at once.
  • ButtonGroupSeparator — the hairline divider between flush items (--acr-border-soft).
  • ButtonGroupText — a non-interactive text / label slot inside the group (e.g. a leading caption like the "Zoom" label, or a static read-only value). For a numeric increment/decrement control, use the dedicated Stepper rather than a −/value/+ button group.

Variants

ButtonGroup has a variant prop:

  • attached (default) — action buttons sharing one rounded gray well (--acr-field), flush, with collapsed inner radii so they read as one capsule. Insert ButtonGroupSeparator between items for the macOS hairlines.
  • ghost — the same flush grouping and dividers as attached, but without the shared background well. Use it when the surrounding surface already provides enough chrome.
  • segmented — a selectable control: the same gray well, but the active item becomes a raised white pill (--acr-control) with a soft shadow. Mark the active item with aria-pressed (or data-active); the others stay plain.
  • split — separate buttons with a small gap and no shared well, still grouped semantically. Use for sub-groups sitting side by side.

Sizes

The kit's Segmented Control ships five sizes — mini / small / medium (default) / large / xl — the same scale as Button and Select: height 16 / 20 / 24 / 28 / 36px, well radius 4 / 5 / 6 / 14 / 18px, font 10 / 11 / 13 / 13 / 13px. Set size once on ButtonGroup; the well, segments, sliding pill and dividers all scale together.

mini · 16px
small · 20px
medium · 24px
large · 28px
xl · 36px
<ButtonGroup variant="segmented" size="small" value={v} onValueChange={setV}>
  <ButtonGroupItem value="day">Day</ButtonGroupItem>
  <ButtonGroupItem value="week">Week</ButtonGroupItem>
</ButtonGroup>

Shape

size controls height, icon scale, text scale, padding and divider inset. shape controls only the corner geometry:

  • auto (default) — follows the macOS kit table. large and xl become capsules.
  • rect — keeps the same size metrics, but uses square-ish corners. This is useful for toolbar groups that need the large hit area without pill-shaped end caps.
  • capsule — forces fully rounded geometry at any size.
<ButtonGroup variant="ghost" size="large" shape="rect">
  <Button icon variant="ghost" size="large" aria-label="Archive">
    <Archive />
  </Button>
  <ButtonGroupSeparator />
  <Button icon variant="ghost" size="large" aria-label="Delete">
    <Trash2 />
  </Button>
</ButtonGroup>

Selection

For value-driven groups, set type (shadcn-style):

  • type="single" (default) — a radio. Use variant="segmented" with ButtonGroupItem children keyed by value, and value/defaultValue/ onValueChange (a string). The raised white pill slides to the selection.
  • type="multiple" — a toggle group ("select any"). Use ButtonGroupToggle children keyed by value, and value/defaultValue/onValueChange (a string array). Selected toggles tint to the accent and several can be on at once — the macOS B/I/U/S formatting toolbar.
// multi-select toggle group
const [marks, setMarks] = React.useState<string[]>(["bold"])

<ButtonGroup type="multiple" value={marks} onValueChange={setMarks}>
  <ButtonGroupToggle value="bold"><Bold /></ButtonGroupToggle>
  <ButtonGroupSeparator />
  <ButtonGroupToggle value="italic"><Italic /></ButtonGroupToggle>
</ButtonGroup>

Notes

  • The well uses --acr-field (the kit's 5% black track, 5% white in dark); the selected pill uses --acr-button-group-control; the hairline uses --acr-button-group-divider. All flip with the theme.
  • Items reuse the Acrylic Button. In the attached well the group renders items flat (it strips each button's own fill/shadow so the single gray surface and the hairline separators show through), so the Button variant you pass doesn't change their look — the well supplies the surface and a faint hover.
  • Disabled works per item across every variant: pass disabled to a Button (attached / split), a ButtonGroupItem (segmented), or a ButtonGroupToggle (multiple). Unavailable items dim to 40% and drop their hover, so a half-enabled group clearly reads which buttons are usable.
  • The kit also ships an accent-blue selected segment (window-focused). The default styling targets the modern macOS 26 white-pill look; switch to an accent fill on the active item if you prefer the focused appearance.
// segmented: active item = raised white pill
<ButtonGroup variant="segmented">
  <Button variant="ghost" aria-pressed={align === "left"}>
    <AlignLeft />
  </Button>
  <Button variant="ghost" aria-pressed={align === "center"}>
    <AlignCenter />
  </Button>
</ButtonGroup>

On this page