Toolbar
Accessible, unstyled WAI-ARIA toolbar with native actions and roving focus.
| Package | @vizejs/ui/toolbar |
| Maturity | stable |
| Own the source | vize lib pull toolbar |
| Requires | context |
| Aliases | toolbar, action toolbar, editor toolbar, command toolbar |
| Covers | WAI-ARIA Toolbar, React Aria Toolbar, Radix Toolbar |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Toolbar, ToolbarItem } from "@vizejs/ui/toolbar";
Or copy the source into your project with vize lib pull toolbar (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Labelled editor toolbar with roving focus that reports which action was pressed. -->
<script setup lang="ts">
import { ref } from "vue";
import { Toolbar, ToolbarItem } from "@vizejs/ui/toolbar";
const lastAction = ref("none");
</script>
<template>
<div>
<Toolbar aria-label="Document actions" @press="(value) => (lastAction = value)">
<ToolbarItem value="save">Save</ToolbarItem>
<ToolbarItem value="duplicate">Duplicate</ToolbarItem>
<ToolbarItem value="publish" disabled>Publish</ToolbarItem>
</Toolbar>
<output>Last action: {{ lastAction }}</output>
</div>
</template>
API
Toolbar
Source: src/families/actions/toolbar/toolbar.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as |
PrimitiveAs |
"div" |
Native element, custom element, or component to render. |
disabled |
boolean |
false |
Disable every item and remove the toolbar from roving focus. |
orientation |
ToolbarOrientation |
"horizontal" |
Directional layout hint used by arrow-key roving focus. |
dir |
ToolbarDirection |
"ltr" |
Reading direction used to map horizontal arrow-key navigation. |
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last enabled item. |
rovingFocus |
boolean |
true |
Whether items participate in a single-tabstop roving focus model. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the toolbar. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the toolbar. |
Events
| Event | Payload | Description |
|---|---|---|
press |
[value: string, nativeEvent: MouseEvent] |
Fired after an enabled item is activated by pointer or keyboard. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ToolbarSlotState |
Renders grouped action controls with toolbar navigation state. |
Exposed
| Member | Type | Description |
|---|---|---|
disabled |
boolean |
Whether the toolbar suppresses every item activation. |
orientation |
ToolbarOrientation |
Directional layout hint used by arrow-key roving focus. |
dir |
ToolbarDirection |
Reading direction used by horizontal arrow-key roving focus. |
rovingFocus |
boolean |
Whether items participate in a single-tabstop roving focus model. |
state |
ToolbarState |
Stable state token for styling and tests. |
style |
ToolbarStyle |
Inline native style object applied to the host. |
activeValue |
string | null |
Currently active item value, or null before an item is focusable. |
element |
PrimitiveElement | null |
Rendered root element or component instance. |
focus |
(options?: FocusOptions) => void |
Move focus to the active or first enabled item. |
focusValue |
(value: string, options?: FocusOptions) => boolean |
Move focus to an enabled item by value and report whether it was found. |
ToolbarItem
Source: src/families/actions/toolbar/toolbar-item.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as |
PrimitiveAs |
"button" |
Native element, custom element, or component to render. |
native |
boolean |
auto |
Whether the rendered target already implements native button semantics. |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
value (required) |
string |
required |
Stable item value emitted by item and toolbar press events. |
disabled |
boolean |
false |
Disable this item while preserving the rest of the toolbar. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label this item. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe this item. |
Events
| Event | Payload | Description |
|---|---|---|
press |
[value: string, nativeEvent: MouseEvent] |
Fired after user activation reaches this enabled item. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ToolbarItemSlotState |
Renders item contents with current availability and navigation state. |
Exposed
| Member | Type | Description |
|---|---|---|
value |
string |
Item value used by the toolbar navigation and press contract. |
disabled |
boolean |
Whether this item or its toolbar suppresses activation. |
orientation |
ToolbarOrientation |
Directional layout hint inherited from the toolbar. |
dir |
ToolbarDirection |
Reading direction inherited from the toolbar. |
state |
ToolbarItemState |
Stable state token for styling and tests. |
element |
PrimitiveElement | null |
Rendered item element or component instance. |
focus |
(options?: FocusOptions) => void |
Move focus to the rendered item. |
Behavior
Every row is proven by a mounted-DOM, SSR/hydration, renderer, type, size, or tree-shaking gate. A row without a passing test is a bug in the family contract.
| ID | Component | Scenario | Behavior | Assertion |
|---|---|---|---|---|
| T1 | toolbar.vue |
semantic root | renders role="toolbar" with labeling, orientation, direction, part, data, and style hooks |
renders accessible toolbar semantics without adding visual CSS |
| T2 | toolbar-item.vue |
vertical roving | one-tabstop arrow-key focus follows orientation and skips disabled native buttons | roving focus follows vertical orientation and skips disabled items |
| T3 | toolbar.vue |
native tab order | rovingFocus=false preserves native tab order and lets arrow keys pass through |
roving focus can be disabled to preserve the native tab order |
| T4 | toolbar-item.vue |
rtl navigation | horizontal arrow-key focus maps next and previous through the toolbar dir contract |
horizontal roving focus respects rtl direction |
| T5 | toolbar-item.vue |
activation | enabled items emit item and toolbar press events in dispatch order |
pointer and keyboard activation emit value-carrying events |
| T6 | both | disabled | disabled toolbars and items suppress activation and leave tab order safely | disabled toolbars and items suppress activation |
| T7 | toolbar-item.vue |
custom element | non-native items receive role="button" and synthesize Enter/Space keyboard clicks |
custom items expose button semantics and keyboard activation |
| T8 | both | public instance | root and items expose focus methods and immutable state snapshots | exposes focus, focusValue, activeValue, and live state |
| T9 | toolbar-item.vue |
duplicate value | mount and reactive value changes reject duplicate item values | rejects duplicate item values before roving focus becomes ambiguous |
| T10 | toolbar-item.vue |
provider boundary | items fail loudly when rendered outside a matching toolbar provider | items require a matching toolbar provider |
| T11 | both | SSR/hydration | server markup is deterministic and hydrates without replacing the root or drifting tabindex | toolbar-ssr.test.ts, runtime-conformance.test.ts |
| T12 | public types | invalid contract | TypeScript rejects unsupported orientation, direction, native button type, and item values | src/families/actions/toolbar/toolbar.types.test-d.ts |
| T13 | root/subpath | consumer bundle | root and subpath consumers retain only Toolbar, emit no CSS, and stay within gzip budget | scripts/check-tree-shaking.mjs |
Contract
Toolbar is a headless compound primitive for clustered actions such as editor
controls, document commands, and inspector panels. It owns no selection model and
emits no visual CSS. Consumers style through data-vize-ui, data-state,
data-disabled, data-orientation, data-roving-focus, data-value, part,
and --vize-ui-toolbar-orientation.
The root always renders role="toolbar". Items are native <button type="button"> controls by default, and non-native targets receive explicit
button semantics plus Enter/Space activation. Roving focus is enabled by default
so the toolbar has a single tab stop; consumers can set rovingFocus=false when
native per-control tab stops are required.
Items require a unique string value so item-level and toolbar-level press
events, activeValue, roving tabindex, and focusValue() stay stable across
polymorphic rendering, slot changes, and DOM reordering. Duplicate values throw
VIZE_UI_TOOLBAR_VALUE_DUPLICATE during registration or reactive value changes
before more than one item can own the active tab stop.