Components / Actions

ExpandingButtonGroup

Icon buttons in a compact group: the one you point at or focus grows to reveal its label while its neighbours slide aside, and an action confirms in place.

Install

npx shadcn@latest add https://irisui.dev/r/expanding-button-group.json
import { ExpandingButtonGroup } from "@/components/iris/expanding-button-group/expanding-button-group";
Registry item expanding-button-groupBrings in foundationRegistry JSON

Preview

ExpandingButtonGroup

Icon buttons in a compact group: the one you point at or focus grows to reveal its label while its neighbours slide aside, and an action confirms in place.

When to use

  • Dense per-item actions where icons alone are ambiguous but labels would crowd the row — message actions, AI response actions (Copy, Edit, Regenerate).
  • Quick actions that should confirm without a toast: set doneLabel ("Archived") and the icon turns into a check.
  • When every label must always be visible, use ButtonGroup. For a floating toolbar with tooltips, use FloatingButtonGroup.

Usage

import { ExpandingButtonGroup } from "@/components/iris/expanding-button-group/expanding-button-group";

<ExpandingButtonGroup
  label="Letter actions"
  items={[
    { id: "reply", label: "Reply", icon: <ReplyIcon size={16} /> },
    {
      id: "archive",
      label: "Archive",
      doneLabel: "Archived",
      icon: <ArchiveIcon size={16} />,
      onSelect: () => api.archiveLetter(id),
    },
    {
      id: "delete",
      label: "Delete",
      doneLabel: "Deleted",
      icon: <TrashIcon size={16} />,
      tone: "danger",
      onSelect: remove,
    },
  ]}
/>;

Props

Prop Type Default Description
items ExpandingButtonGroupItem[] — Required: { id, label, icon, onSelect?, doneLabel?, tone?, disabled? }. onSelect may return false or reject to skip the confirmation; a promise keeps the button busy.
label string — Accessible name of the toolbar (required).
size "sm" | "md" "md" Button size.
defaultExpanded string | null first enabled Id of the action whose label shows at rest. One action is always expanded; it returns here when pointer and focus leave.
onAction (id: string) => void — Called with the item id before the item's own onSelect.
confirmDuration number 1400 How long the done state stays, in ms.
className, style — — Passed to the root.

Notes

  • Renders role="toolbar" with a roving tab stop; Left/Right, Home and End move focus, and focus expands the focused action.
  • Labels are always the accessible names, collapsed or not. A polite live region (<output>) announces the done label; busy actions set aria-busy.
  • tone: "danger" colours the expanded state with --red. Disabled actions stay focusable and still reveal their label.
  • Icons are sized by the group; a 16px icon fits.