Bulk Action Bar
Admin bulk action bar compound — sticky or inline toolbar that appears when rows are selected. Shows a live-announced selection count, an actions slot, and an optional dismiss button. Built with CSS Modules and a reduced-motion-aware entrance animation — copy it into your project and own it completely.
Inline
The inline variant flows in the document instead of pinning to the viewport bottom — useful when the bar lives inside a scroll container or a dialog.
Custom Label
Provide a label renderer to customize the count string — useful for pluralization or domain-specific nouns.
Without Dismiss
Set dismissible={false} when the selection should only be cleared programmatically by consumer actions.
Installation
npx visor add bulk-action-barThis copies two files into your project:
components/ui/bulk-action-bar/bulk-action-bar.tsx— the componentcomponents/ui/bulk-action-bar/bulk-action-bar.module.css— the styles
The registry also pulls in button as a dependency.
Usage
import { useState } from 'react';
import { BulkActionBar } from '@/components/ui/bulk-action-bar/bulk-action-bar';
import { Button } from '@/components/ui/button/button';
export default function Example() {
const [selected, setSelected] = useState<string[]>([]);
return (
<BulkActionBar
count={selected.length}
onClear={() => setSelected([])}
>
<Button variant="outline" size="sm">
Archive
</Button>
<Button variant="destructive" size="sm">
Delete
</Button>
</BulkActionBar>
);
}API Reference
BulkActionBarProps
| Prop | Type | Default | Description |
|---|---|---|---|
count* | number | — | Number of selected items. The bar returns null when count is 0 or less. |
children* | React.ReactNode | — | Action buttons cluster — typically one or more Button instances. |
inline | boolean | false | Render inline (non-sticky) instead of fixed to the viewport bottom. |
label | (count: number) => React.ReactNode | (n) => `${n} selected` | Renderer for the selection count label. Wrapped in aria-live="polite" so changes are announced. |
clearLabel | React.ReactNode | 'Clear selection' | Aria-label for the dismiss button. Used verbatim when it is a string. |
onClear | () => void | — | Fired by the Escape key and the dismiss button. The Escape handler is only attached when this is provided. |
dismissible | boolean | true | Show the dismiss (X) button. Requires onClear to render the button. |
autoFocus | boolean | true | Auto-focus the first enabled action button on mount. Only runs on mount, not on count changes. |
The component also accepts all standard HTML attributes for the root <div>.
Behavior
- Mounts only when
count > 0— zero selection renders nothing, no invisible DOM. - Escape-to-clear — when
onClearis provided, pressing Escape anywhere on the page fires it. - Auto-focus — on mount, focus moves to the first enabled action button. Disable with
autoFocus={false}. - Entrance animation — sticky variant slides up + fades in over 300ms.
prefers-reduced-motionskips the animation entirely. - Live announcement — the count is wrapped in
aria-live="polite"so assistive tech announces selection changes.
Source Files
After running npx visor add bulk-action-bar, you'll have:
bulk-action-bar.tsx
A forwardRef client component with role="toolbar" and aria-label="Bulk actions". The count span is aria-live="polite", the Escape handler is attached via useEffect only when onClear is supplied, and auto-focus runs once on mount via a useRef to the actions slot.
bulk-action-bar.module.css
All values use CSS custom properties from @loworbitstudio/visor-core. The sticky variant uses position: fixed with z-index: 50 (promote to a --z-index-toolbar token once the z-index scale is formalized), and the entrance keyframes are wrapped in a prefers-reduced-motion guard.
Customization
After copying the component, you own it completely. Common customizations:
- Anchor the sticky variant inside a scroll container by swapping
position: fixedforposition: sticky. - Replace the default count label with a translation function that handles plural forms.
- Add a "Select all" affordance next to the count for cross-page selection.
- Promote the bar into a Portal when the surrounding layout clips fixed children.
Activity Feed
Admin activity feed compound — vertical ordered list of timestamped events for dashboards, audit logs, and notification views. ActivityFeed + ActivityFeedItem with leading, title, description, actor, timestamp, and trailing slots. Default, compact, and timeline variants. Built with CSS Modules and React Context — copy it into your project and own it completely.
ChromeButton
A 28px-tall button primitive for topbar and chrome contexts with an optional leading icon and trailing Kbd shortcut hint. Built with CSS Modules — copy it into your project and own it completely.