A desktop application menu bar (File, Edit, View) with keyboard navigation across and within its menus.
Menubar is the WAI-ARIA menubar pattern on the recessed control fill. It is one tab stop: Left and Right move across the titles, Down, Enter or Space open a menu at its first item and Up at its last. While a menu is open, Left and Right move to the neighbouring menu and hovering another title switches to it.
Each menu is the shared menu list: icons, shortcut hints, separators, headings and danger items in the popover glass.
inline renders the open panel in the page flow: no portal, scrim, focus trap, scroll lock or outside-click listener. It exists for docs, previews and static mock-ups; never ship it as the live overlay.
Installation
npx shadcn@latest add KezLahd/rime/menubarAlso installs: base, context-menu, popover.
Usage
import { Menubar } from "@/components/ui";
<Menubar
aria-label="Editor"
menus={[
{ id: "file", label: "File", items: [{ id: "new", label: "New project", shortcut: "Ctrl N", onSelect: create }] },
{ id: "edit", label: "Edit", items: [{ id: "undo", label: "Undo", shortcut: "Ctrl Z", onSelect: undo }] },
]}
/>Examples
Open
Interactive
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
Menubar
| Prop | Type | Default | Description |
|---|---|---|---|
| menusRequired | ReadonlyArray<MenubarMenu> | ||
| aria-labelRequired | string | The bar's accessible name, e.g. "Editor". | |
| inline | boolean | Render one menu open in the page flow under its trigger (docs, previews). Nothing is interactive and no listeners are attached. | |
| inlineOpenId | string | Inline only: which menu is drawn open (default the first) and which item is highlighted. | |
| inlineHighlightId | string | ||
| className | string |
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
| Token | Default | Live value |
|---|---|---|
| --menu-list-min-width | 220px | … |
| --menubar-bg | rgba(var(--rgb-white), 0.5) | … |
| --menubar-item-radius | var(--r-sm) | … |
| --menubar-radius | var(--r-md) | … |
| --popover-item-active-bg | linear-gradient(to right, rgba(var(--rgb-brand), 0.14), rgba(var(--rgb-brand-deep), 0.07)) | … |
| --popover-item-press-bg | linear-gradient(to right, rgba(var(--rgb-brand), 0.2), rgba(var(--rgb-brand-deep), 0.1)) | … |
| --popover-item-radius | var(--r-xs) | … |
| --popover-pad | calc(6px * var(--density)) | … |
| --popover-radius | var(--r-lg) | … |
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --blur-recess | tokens.css | … |
| --border-w | tokens.css | … |
| --control-h-sm | tokens.css | … |
| --danger-ink | tokens.css | … |
| --density | tokens.css | … |
| --dur | tokens.css | … |
| --dur-fast | tokens.css | … |
| --dur-slow | tokens.css | … |
| --ease | tokens.css | … |
| --ease-out | tokens.css | … |
| --focus-ring | tokens.css | … |
| --font-body | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-heading | tokens.css | … |
| --ink-muted | tokens.css | … |
| --ink-secondary | tokens.css | … |
| --line-soft | tokens.css | … |
| --popover-blur | tokens.css | … |
| --popover-border | tokens.css | … |
| --popover-shadow | tokens.css | … |
| --popover-surface | tokens.css | … |
| --r-lg | tokens.css | … |
| --r-md | tokens.css | … |
| --r-sm | tokens.css | … |
| --r-xs | tokens.css | … |
| --rgb-brand | tokens.css | … |
| --rgb-brand-deep | tokens.css | … |
| --rgb-danger | tokens.css | … |
| --rgb-white | tokens.css | … |
| --shadow-recess | tokens.css | … |
| --text-caption | tokens.css | … |
| --text-micro | tokens.css | … |
| --text-small | tokens.css | … |
| --tracking-label | tokens.css | … |
| --wash-active | tokens.css | … |
| --wash-hover | tokens.css | … |
| --z-popover | tokens.css | … |
Accessibility
- role="menubar" holding role="menuitem" titles with aria-haspopup and aria-expanded; each menu is role="menu" labelled by its title.
- Roving tab index: the bar is a single tab stop. Esc closes a menu and returns focus to its title; Tab closes it and moves on.
Do and don't
Do
- Use it for desktop tools and editors with many commands.
- Mirror the real keyboard shortcuts in the hints.
Don't
- Don't use it for site navigation; that is NavigationMenu or the sidebar.
- Don't nest submenus; keep each menu flat.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open menubar.mdRelated: context-menu, menu, command