Menubar
The <x-wirekit::menubar> component creates a horizontal menu bar with dropdown menus, following the WAI-ARIA Menubar pattern. It is designed for desktop application-style interfaces with menus like File, Edit, View.
Usage
Basic Setup
<x-wirekit::menubar>
<x-wirekit::menubar.menu label="File">
<x-wirekit::menubar.item shortcut="Ctrl+N">New File</x-wirekit::menubar.item>
<x-wirekit::menubar.item shortcut="Ctrl+O">Open</x-wirekit::menubar.item>
<x-wirekit::menubar.item shortcut="Ctrl+S">Save</x-wirekit::menubar.item>
<x-wirekit::menubar.separator />
<x-wirekit::menubar.item :danger="true">Exit</x-wirekit::menubar.item>
</x-wirekit::menubar.menu>
<x-wirekit::menubar.menu label="Edit">
<x-wirekit::menubar.item shortcut="Ctrl+Z">Undo</x-wirekit::menubar.item>
<x-wirekit::menubar.item shortcut="Ctrl+Y">Redo</x-wirekit::menubar.item>
<x-wirekit::menubar.separator />
<x-wirekit::menubar.item shortcut="Ctrl+X">Cut</x-wirekit::menubar.item>
<x-wirekit::menubar.item shortcut="Ctrl+C">Copy</x-wirekit::menubar.item>
<x-wirekit::menubar.item shortcut="Ctrl+V">Paste</x-wirekit::menubar.item>
</x-wirekit::menubar.menu>
<x-wirekit::menubar.menu label="View">
<x-wirekit::menubar.item>Zoom In</x-wirekit::menubar.item>
<x-wirekit::menubar.item>Zoom Out</x-wirekit::menubar.item>
<x-wirekit::menubar.separator />
<x-wirekit::menubar.item>Full Screen</x-wirekit::menubar.item>
</x-wirekit::menubar.menu>
</x-wirekit::menubar>
Items with Links
Items can act as navigation links. The example below ships three top-level menus to mirror the canonical macOS / shadcn menubar pattern — a single-trigger menubar reads as a "button-in-a-button" because the parent border has no other menus to balance against.
Danger Items
Use the danger prop for destructive actions:
Disabled items are visually muted, not focusable via keyboard navigation, and have aria-disabled="true".
Keyboard Shortcuts Display
The shortcut prop renders a secondary label on the right side of the item. It is display-only and does not register actual keyboard handlers:
To register real keyboard shortcuts, use Alpine or Livewire listeners separately.
Submenus
Nest a <x-wirekit::menubar.submenu> inside a menu to add a flyout sub-menu (e.g. "Export As → PDF / PNG"). The parent item opens a child panel beside it (on hover, click, or ArrowRight); the child holds ordinary <x-wirekit::menubar.item>s.
The parent carries aria-haspopup="menu" + aria-expanded; the child panel is its own role="menu". ArrowRight on a submenu parent opens the submenu (rather than moving to the next top-level menu); ArrowLeft / Escape close it. Submenus nest arbitrarily deep.
Behavior
- Hover-to-switch: when one menu is open, hovering another menu trigger opens it immediately (no click needed)
- Floating UI (bundled ~3.5 KB) positions menus below their triggers with flip and shift
- Click outside closes any open menu
- Livewire SPA navigation (
wire:navigate) automatically closes open menus - Transitions use scale + opacity for smooth open/close animation
Props
<x-wirekit::menubar>
| Prop | Type | Default | Description |
|---|---|---|---|
scope |
string|null |
null |
Scoped personalization key |
<x-wirekit::menubar.menu>
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string |
required | Visible menu trigger text (e.g. "File", "Edit") |
scope |
string|null |
null |
Scoped personalization key |
<x-wirekit::menubar.item>
| Prop | Type | Default | Description |
|---|---|---|---|
href |
string|null |
null |
URL (renders <a> instead of <button>) |
danger |
bool |
false |
Destructive/danger styling |
disabled |
bool |
false |
Disabled state |
shortcut |
string|null |
null |
Keyboard shortcut display text (visual only) |
scope |
string|null |
null |
Scoped personalization key |
<x-wirekit::menubar.submenu>
| Prop | Type | Default | Description |
|---|---|---|---|
label |
string|null |
null |
Parent item text (or pass a <x-slot:label> for rich content) |
icon |
string|null |
null |
Optional leading icon (WireKit icon alias) |
placement |
string |
'right-start' |
Floating UI placement of the child panel |
offset |
int |
0 |
Distance in px between the parent item and the child panel |
disabled |
bool |
false |
Disabled state |
scope |
string|null |
null |
Scoped personalization key |
Sub-Components
| Component | Purpose |
|---|---|
menubar.menu |
A single top-level menu trigger with its dropdown |
menubar.item |
A menu entry inside a menu (link or action) |
menubar.submenu |
A nested flyout sub-menu opened from a menu item |
menubar.separator |
Visual divider between item groups |
Accessibility
- Menubar wrapper:
role="menubar", horizontal orientation - Menu triggers:
role="menuitem",aria-haspopup="menu",aria-expanded(dynamic) - Menu panels:
role="menu",aria-labelfrom thelabelprop - Menu items:
role="menuitem",tabindex="-1" - Disabled items:
aria-disabled="true", skipped by keyboard navigation - Roving tabindex: only the active menu trigger in the bar has
tabindex="0"; others havetabindex="-1" - Focus management: opening a menu focuses the first item; closing returns focus to the trigger
- Icons:
aria-hidden="true"(decorative)
Keyboard Interaction
The menubar implements the full WAI-ARIA Menubar keyboard pattern:
| Key | Context | Action |
|---|---|---|
| Tab | Menubar | Move focus into / out of the menubar |
| Arrow Left / Right | Menubar | Move focus between top-level menu triggers |
| Enter / Space | Menu trigger | Open the menu, focus first item |
| Arrow Down | Menu trigger | Open the menu, focus first item |
| Arrow Up | Menu trigger | Open the menu, focus last item |
| Arrow Down / Up | Open menu | Move to next / previous item |
| Home / End | Open menu | Jump to first / last item |
| Enter / Space | Menu item | Activate the focused item |
| Escape | Open menu | Close menu, return focus to menu trigger |
| Arrow Left / Right | Open menu | Close current, open adjacent menu |
| Arrow Right | Submenu parent | Open the submenu and focus its first item |
| Arrow Left / Escape | Inside a submenu | Close the submenu, return focus to the parent item |
Pitfalls
- Don't use menubar for application-wide navigation. WAI-ARIA Authoring Practices reserve menubars for app-internal commands (Edit > Copy). For site navigation reach for
<x-wirekit::navigation-menu>. - Don't combine menubar with a sidebar nav. Two competing keyboard wayfinding patterns exhaust focus management — pick one.
Design Tokens
| Element | Token |
|---|---|
| Bar background | --color-wk-bg-elevated |
| Bar border | --color-wk-border / --border-wk-width |
| Trigger text | --color-wk-text |
| Trigger hover bg | --color-wk-bg-subtle |
| Trigger active bg | --color-wk-bg-subtle |
| Trigger radius | --radius-wk-md |
| Menu background | --color-wk-bg-elevated |
| Menu border | --color-wk-border |
| Menu radius | --radius-wk-lg |
| Menu shadow | --shadow-wk-lg |
| Item text | --color-wk-text |
| Item hover bg | --color-wk-bg-subtle |
| Item radius | --radius-wk-md |
| Danger text | --color-wk-danger-text |
| Shortcut text | --color-wk-text-muted |
| Separator color | --color-wk-border-subtle |
| Font family | --font-wk-sans |
| Font size | --text-wk-md |
| Disabled opacity | --opacity-wk-disabled |
| Transition | --transition-wk-duration |
Personalization
Override defaults in config/wirekit.php:
'components' => [
'menubar' => [],
],
Scoped Personalization
<x-wirekit::menubar scope="editor-toolbar">
...
</x-wirekit::menubar>
'personalizations' => [
'menubar' => [
'editor-toolbar' => [
'base' => 'border-b-2',
],
],
],
Further Reading
- WAI-ARIA Menubar Pattern -- the authoring pattern this component implements
- WAI-ARIA Menu Pattern -- individual dropdown menus
- MDN:
role="menubar" - MDN:
role="menu" - MDN:
role="menuitem" - Floating UI -- positioning engine (bundled, ~3.5 KB)