Action Bar
Floating contextual bar — a status region and actions, for bulk selection, unsaved changes, and batch progress.
- Status
- Since
0.4.0- Accessibility pattern
- APG toolbar
Last updated
Install
Add Action Bar from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/action-barThe same command installs the registry items it composes: @vegastack/button.
Usage
import {
ActionBar,
ActionBarButton,
ActionBarSeparator,
} from "@/components/ui/action-bar";
<ActionBar open={count > 0} status={`${count} selected`}>
<ActionBarButton onClick={tag}>Tag</ActionBarButton>
<ActionBarSeparator />
<ActionBarButton
render={<Button variant="soft" tone="destructive" size="sm" />}
onClick={archive}
>
Archive
</ActionBarButton>
</ActionBar>;Bulk selection is the bar's most common recipe — never its identity. The same object serves an unsaved-changes bar and a batch-progress bar; only the words change.
Compose the actions as ActionBarButton. The bar is a Base UI Toolbar, and a
toolbar builds its single tab stop from the items that register with it — a bare
<Button> still renders, but keeps its own tab stop and the arrow keys skip it.
Pass render when an action needs a different shape (a destructive button, an
IconButton, a menu trigger).
Scope
| Behaviour | Where it lives |
|---|---|
| Selection state | The host's list — DataList owns selectedIds; the bar consumes a count |
| The actions themselves | Host, as ActionBarButton children (render a Button, a DropdownMenu trigger, an AlertDialog trigger) |
| Outcome persistence | The status slot — not a toast, which discards state the user still holds |
Anatomy
Action Bar is a compound component. Every exported part, with the
data-slot it renders (generated from the canonical source):
Examples
Unsaved changes
Batch progress (pending)
While pending, the actions are inert and dimmed but the status stays
readable — a bulk operation in flight should not be re-triggerable.
API Reference
ActionBar
| Prop | Type | Default | Description |
|---|---|---|---|
announcement | string | — | Text announced politely when it changes. Defaults to status when that is
a plain string; pass explicitly when status is composite markup. |
aria-label | string | "Actions" | Accessible name for the toolbar. |
children | React.ReactNode | — | The actions. Compose ActionBarButton and ActionBarSeparator: Base UI's toolbar
builds its single tab stop from the items that register with it, so a bare <Button> renders
but keeps its own tab stop and the arrow keys skip it. |
containerRef | React.RefObject<HTMLElement | null> | — | Centre the bar over this element instead of the viewport — measured via
getBoundingClientRect, kept live through ResizeObserver plus window
resize/scroll, so a content area beside a sidebar gets a truly centred
bar. (A container that moves without any resize or scroll event — a
transition-driven layout shift — re-measures on the next of either.) |
open | boolean | true | Whether the bar is shown. It stays mounted while hidden (translated below
the viewport edge, data-active="false"), so the enter/exit transition is
pure CSS and the live region survives. |
pending | boolean | false | Dim and inert the actions while a bulk operation is in flight
(aria-busy + non-interactive), keeping the status region readable. |
status | React.ReactNode | — | The status region — a count ("5 selected"), a state ("Unsaved changes"), or progress ("Importing 340 of 1,000…"). Rendered before the actions. |
Data attributes and CSS variables on ActionBar
| Attribute | Values |
|---|---|
data-active | "false" | "true" |
data-pending | "" |
data-slot | "action-bar" | "action-bar-actions" | "action-bar-status" |
ActionBarButton
One action, registered with the bar's roving focus. Takes every
Base UI Toolbar.Button prop;
render defaults to a ghost sm Button.
ActionBarSeparator
The hairline between two clusters of actions. Takes every Base UI
Toolbar.Separator prop; the orientation defaults to perpendicular to the bar,
so a horizontal bar gets a vertical rule.
Accessibility
- The bar is a Base UI
Toolbarlabelled byaria-label— pass a specific one ("Bulk actions", "Import progress") rather than the generic default. The whole bar is one tab stop: the arrow keys move between actions and wrap at the ends, and Shift + Tab leaves in a single press. - Status changes are announced through a polite live region — a plain-string
statusannounces itself; composite status markup needs anannouncement. - Actions with visible text must not carry
aria-label(WCAG 2.5.3) — the visible text is the name. - The bar sits in the
raisedband, so a dialog opened from one of its actions correctly covers it. While hidden (and whilepending, for the actions) the subtree isinert— nothing invisible or in-flight stays focusable or activatable.
| Key | Action |
|---|---|
| Tab | Enter the bar at the last-focused action, or leave it. |
| ← / → | Move between actions; focus wraps at either end. |
| Shift + Tab | Leave the bar in one press. |
| Enter / Space | Activate the focused action. |
| Contract | States tested |
|---|---|
| Behaviour | open, hidden, pending, measured-centring |
| Accessibility | labeled, status-announcement, keyboard, semantic-html |
| Visual | default, hidden, pending, dark |