Xzero UI
Pro library

Expanding button group

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.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui/expanding-button-group
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui/expanding-button-group

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { Archive, Clock3, Forward, Reply, Trash2 } from "lucide-react";
import { ExpandingButtonGroup } from "@/components/xzero/expanding-button-group/expanding-button-group";

export function MessageActions({ message }: { message: Message }) {
  return (
    <ExpandingButtonGroup
      label="Message actions"
      defaultExpanded="reply"
      items={[
        { id: "reply", label: "Reply", icon: <Reply />, onSelect: () => openReply(message.id) },
        { id: "forward", label: "Forward", icon: <Forward />, onSelect: () => openForward(message.id) },
        { id: "archive", label: "Archive", doneLabel: "Archived", icon: <Archive />, onSelect: () => archive(message.id) },
        { id: "snooze", label: "Snooze", doneLabel: "Snoozed", icon: <Clock3 />, onSelect: () => snooze(message.id, "tomorrow-8am") },
        { id: "delete", label: "Delete", doneLabel: "Deleted", tone: "danger", icon: <Trash2 />, onSelect: () => moveToTrash(message.id) },
      ]}
    />
  );
}

API reference

ExpandingButtonGroup

Icon buttons in a compact group where one action always shows its label. The one you point at or focus takes the label over while its neighbours slide aside, and an action confirms in place.

PropTypeDefaultDescription
items (required){ id: string; label: string; icon: ReactNode; onSelect?: () => void | boolean | Promise<void | boolean>; doneLabel?: string; tone?: "neutral" | "danger"; disabled?: boolean }[]–The actions, in order. label is shown when the button expands and is always its accessible name. With doneLabel, a successful onSelect turns the icon into a check and the label into that word, such as "Archived". Return false or reject to skip it; a promise keeps the button busy until it settles.
label (required)string–Accessible name of the toolbar, such as "Message actions".
size"sm" | "md""md"Button height of 28 or 36px, with 14 or 16px icons.
defaultExpandedstring | nullFirst enabled actionId of the action whose label shows at rest, usually the primary one. One action is always expanded: pointing at or focusing another moves the expansion there, and the group returns to this one when the pointer and focus leave.
onAction(id: string) => void–Called with the item id whenever an action runs, before the item's own onSelect.
confirmDurationnumber1400How long the confirmation stays, in milliseconds.
classNamestring–Extra class on the toolbar.
styleCSSProperties–Inline styles on the toolbar.

Keyboard interactions

KeysAction
TabMoves into the toolbar onto one action (the last focused, else the resting one) and out again.
Arrow left / Arrow rightMoves focus to the previous or next action, wrapping at the ends. The focused action expands to show its label.
Home / EndMoves focus to the first or last action.
Enter / SpaceRuns the focused action.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in buttons

Guidance for AI tools

Expanding button group: 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. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components