Skip to main content
WireKit
Copy for LLM

Action Bar

The <x-wirekit::action-bar> component creates a floating toolbar that appears at the bottom of the viewport — typically used for bulk actions when one or more items are selected in a list or table. It uses role="group" — a named set of related controls, each reached with Tab — and announces its presence to screen readers via a live region.

Usage

The preview below uses mode="static" so the bar renders inline within the docs preview frame. The default mode="floating" pins the bar to the bottom-center of the viewport via position: fixed — see Layout Modes for the full positioning details and when to choose each.

Action Bar (static mode)
Bulk actions available
3 items selected

With Livewire state:

<x-wirekit::action-bar :visible="$selectedCount > 0">
    <span class="text-[var(--color-wk-text-muted)]">{{ $selectedCount }} items selected</span>
    <x-wirekit::button size="sm" wire:click="archiveSelected">Archive</x-wirekit::button>
    <x-wirekit::button size="sm" intent="danger" wire:click="deleteSelected">Delete</x-wirekit::button>
</x-wirekit::action-bar>

With Livewire

The visible prop is typically bound to a Livewire computed property or state. The bar appears when items are selected and disappears when the selection is cleared.

{{-- In your Livewire component --}}
<x-wirekit::action-bar :visible="count($this->selected) > 0">
    <span class="text-[length:var(--text-wk-sm)] text-[var(--color-wk-text-muted)]">
        {{ count($this->selected) }} selected
    </span>

    <x-wirekit::button size="sm" wire:click="exportSelected">
        Export
    </x-wirekit::button>

    <x-wirekit::button size="sm" intent="neutral" surface="ghost" wire:click="clearSelection">
        Clear
    </x-wirekit::button>

    <x-wirekit::button size="sm" intent="danger" wire:click="deleteSelected">
        Delete
    </x-wirekit::button>
</x-wirekit::action-bar>

Custom Content

The action bar accepts any content in its default slot — buttons, text, badges, or any other components:

<x-wirekit::action-bar :visible="$hasUnsavedChanges">
    <x-wirekit::icon name="warning" class="text-[var(--color-wk-warning-text)]" />
    <span>You have unsaved changes</span>
    <x-wirekit::button size="sm" wire:click="save">Save</x-wirekit::button>
    <x-wirekit::button size="sm" intent="neutral" surface="ghost" wire:click="discard">Discard</x-wirekit::button>
</x-wirekit::action-bar>

Alpine-Controlled Visibility

You can also control visibility with Alpine instead of server-side Livewire state:

<div x-data="{ selected: [] }">
    {{-- Table with checkboxes that populate selected[] --}}

    <x-wirekit::action-bar x-show="selected.length > 0" :visible="true">
        <span x-text="selected.length + ' items selected'"></span>
        <x-wirekit::button size="sm" @click="bulkAction()">Process</x-wirekit::button>
    </x-wirekit::action-bar>
</div>

When using Alpine's x-show for visibility, set :visible="true" on the component so the server renders the bar in the DOM. Alpine will then control its display state client-side.

The built-in announcement is server-driven A live region speaks when its text CHANGES while the region is already on the page. With :visible="true" the bar's text is there from first paint and Alpine only flips display, so the bar's own region stays silent and the announcement is yours to make — write the wording into an aria-live="polite" element of your own when selected.length crosses zero. Server-driven visibility (:visible="$hasSelection" re-rendered by Livewire) announces on its own, because the flip is what changes the text.

Layout Modes

The action bar ships in two layout modes, controlled by the mode prop. Both share the same chrome — bordered, elevated, rounded, padded — and only the positioning differs.

mode="floating" (default)

Pinned to the bottom center of the viewport via position: fixed:

  • position: fixed with bottom spacing from --padding-wk-y-lg
  • Horizontally centered via left: 50% + transform: translateX(-50%)
  • z-index from --z-wk-sticky — sits above page content but below modals and drawers

This is the default mode and the right choice for list pages where the bar should hover over the content while the user scrolls. Visible regardless of scroll position.

mode="static"

Renders inline with surrounding content (inline-flex) — drops the fixed positioning and the viewport-centering transforms, keeps the same visual chrome:

<x-wirekit::action-bar mode="static" :visible="true">
    <span>3 selected</span>
    <x-wirekit::button size="sm">Archive</x-wirekit::button>
    <x-wirekit::button size="sm" intent="danger">Delete</x-wirekit::button>
</x-wirekit::action-bar>

Use this mode when the bar belongs to a card / panel / dashboard rather than a viewport-floating overlay — for example a "selected items" toolbar inside a section, or a contextual bar inside a tab. Composes cleanly with normal flow layouts (flex, grid, document order).

Behavior

  • Server-rendered visibility — when visible is false, the component renders with display: none
  • Livewire-friendly — the visible prop re-evaluates on every Livewire render cycle, so the bar appears/disappears automatically as the selection state changes
  • Slot-based — the action bar has no opinion about its content; you fill it with buttons, text, or any other components

Props

Prop Type Default Description
visible bool false Whether the action bar is currently visible
mode string 'floating' Layout mode: 'floating' (viewport-pinned, the default) or 'static' (inline)
scope string|null null Scoped personalization key

Accessibility

  • Container: role="group" — announces the bar as one named set of related controls
  • aria-label="Bulk actions" — provides an accessible name describing the group's purpose
  • Live region: aria-live="polite" announces "Bulk actions available" to screen readers when the server renders the bar visible (see the note under Alpine-Controlled Visibility for the client-driven case)
  • All buttons inside the bar are natively keyboard-accessible
  • Focus ring visible on all interactive elements
  • Every string the bar speaks is translated — run php artisan vendor:publish --tag=wirekit-lang to override the wording

Why group and not toolbar role="toolbar" is a promise about the keyboard: one tab stop for the whole bar, arrow keys moving between the controls inside it. The action bar binds no keys — it is a layout wrapper, and its children are ordinary buttons that Tab reaches one by one. role="group" describes exactly that. If you want the arrow-key model, use <x-wirekit::toolbar>.

Keyboard Interaction

This component is a layout wrapper. Keyboard interaction is delegated to its children.

Pitfalls

  • Don't use action-bar as a permanent UI. It's purpose-built for transient bulk-action contexts (selected rows). For persistent action surfaces use <x-wirekit::toolbar>.

Design Tokens

Token Used for
--font-wk-sans Action-bar font family
--text-wk-md Action-bar font size
--color-wk-text Action-bar text
--color-wk-bg-elevated Action-bar background
--color-wk-border Action-bar border
--border-wk-width Border width
--radius-wk-xl Pill-shaped border radius
--shadow-wk-lg Floating-mode drop shadow
--gap-wk-md Gap between action buttons
--padding-wk-x-lg / --padding-wk-y-sm / --padding-wk-y-lg Container padding
--z-wk-sticky Floating-mode stacking context

Personalization

Override classes globally via WireKit::personalize():

use Pushery\WireKit\WireKit;

WireKit::personalize('action-bar', [
    'base' => 'fixed bottom-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-4 px-6 py-3 bg-gray-900 text-white rounded-full shadow-2xl',
]);

Further Reading

Was this page helpful?

Voting requires cookies or local storage. What we store