Components Navigation
Menubar
StableA persistent bar of menus for editor-style applications, as in File, Edit and View.
Use Menubar in dense, application-like tools such as a signature template editor, where users expect desktop-style menus. It implements the WAI-ARIA menubar pattern: one tab stop for the whole bar, arrow keys between menus, and ArrowLeft/ArrowRight to move between open menus. For website navigation use NavigationMenu instead.
Overview
import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from '@bindshq/stance-react';
export function Basic() {
return (
<Menubar aria-label="Template editor">
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>
New template <MenubarShortcut>⌘N</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Save <MenubarShortcut>⌘S</MenubarShortcut>
</MenubarItem>
<MenubarSub>
<MenubarSubTrigger>Export</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>HTML</MenubarItem>
<MenubarItem>PNG image</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>Close editor</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem>
Undo <MenubarShortcut>⌘Z</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Redo <MenubarShortcut>⇧⌘Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>Insert placeholder field</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Insert</MenubarTrigger>
<MenubarContent>
<MenubarItem>Logo</MenubarItem>
<MenubarItem>Social icons</MenubarItem>
<MenubarItem>Promotional banner</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubTrigger, MenubarSubContent } from '@bindshq/stance-react';Examples
import * as React from 'react';
import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarTrigger } from '@bindshq/stance-react';
export function WithStateItems() {
const [ruler, setRuler] = React.useState(true);
const [device, setDevice] = React.useState('desktop');
return (
<Menubar aria-label="Preview">
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem checked={ruler} onCheckedChange={setRuler}>
Show ruler
</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarRadioGroup value={device} onValueChange={setDevice}>
<MenubarRadioItem value="desktop">Desktop client</MenubarRadioItem>
<MenubarRadioItem value="mobile">Mobile client</MenubarRadioItem>
<MenubarRadioItem value="web">Outlook on the web</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarContent>
</MenubarMenu>
</Menubar>
);
}Controlled
Control which menu is open with value / onValueChange.
Open menu: none
import * as React from 'react';
import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from '@bindshq/stance-react';
export function Controlled() {
const [open, setOpen] = React.useState<string | null>(null);
return (
<div style={{ display: 'grid', gap: 12, justifyItems: 'start' }}>
<Menubar aria-label="Document" value={open} onValueChange={setOpen}>
<MenubarMenu value="document">
<MenubarTrigger>Document</MenubarTrigger>
<MenubarContent>
<MenubarItem>Rename</MenubarItem>
<MenubarItem>Version history</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu value="share">
<MenubarTrigger>Share</MenubarTrigger>
<MenubarContent>
<MenubarItem>Invite collaborators</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
<span style={{ color: 'var(--st-color-fg-muted)', fontSize: 'var(--st-font-size-sm)' }}>
Open menu: {open ?? 'none'}
</span>
</div>
);
}API reference
Menubar
| Prop | Type | Default | Description |
|---|---|---|---|
value / defaultValue | string | null | None | The open menu’s value (controlled or initial). |
onValueChange | (value: string | null) => void | None | Called when a menu opens or closes. |
loop | boolean | true | Wrap focus between first and last menus. |
aria-label | string | None | Name of the menubar, e.g. “Template editor”. |
MenubarMenu
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | None | Identifier for controlled usage. Generated by default. |
MenubarTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
...props | ButtonHTMLAttributes | None | The top-level button (role menuitem). |
MenubarContent
| Prop | Type | Default | Description |
|---|---|---|---|
align / sideOffset | Align / number | 'start' / 6 | Placement of the menu under its trigger. |
MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, …
| Prop | Type | Default | Description |
|---|---|---|---|
None | None | None | Identical to their DropdownMenu counterparts. |
Accessibility
- The bar has role menubar and a single tab stop (roving tabindex) across its triggers.
- Triggers are menuitems with aria-haspopup, aria-expanded and aria-controls.
- With a menu open, ArrowLeft/ArrowRight close it and open the neighbouring menu with focus on its first item.
- Hovering across the bar while a menu is open switches menus, matching desktop conventions.
Keyboard
| Keys | Action |
|---|---|
| ArrowLeft ArrowRight | Move between triggers, or between open menus. |
| Home End | First or last trigger. |
| Enter Space ArrowDown | Open the menu and focus its first item. |
| ArrowUp | Open the menu and focus its last item. |
| Escape | Close the menu and return focus to its trigger. |
Usage guidelines
Do
- Reserve for application-like editors with many commands.
- Show keyboard shortcuts for frequent commands.
Don’t
- Use for marketing or documentation site navigation.