Installation
Usage
- React
- Vue
Menubar
Setmenubar (React) or :menubar="true" (Vue) on Menu.Root to create a menubar:
API Reference
Root
The root container for the menu.| Prop | Type | Default | Description |
|---|---|---|---|
menubar | boolean | false | Menubar mode |
Trigger
The button that opens the menu or submenu. No props.Popover
The menu container. Renders as<ul role="menu"> (or role="menubar" in menubar mode).
No props.
Item
A menu action. Renders as<button>, <a> (with href), or <span> (with disabled).
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Non-interactive, skipped by keyboard |
href | string | — | Renders as <a> instead of <button> |
Click closes the menu. Use
e.stopPropagation() in the click handler to prevent closing.CheckboxItem
A toggle menu item withrole="menuitemcheckbox".
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | false | Checked state |
disabled | boolean | false | Non-interactive, skipped by keyboard |
Click toggles
aria-checked, menu stays open.RadioItem
A radio menu item withrole="menuitemradio".
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | false | Selected state |
disabled | boolean | false | Non-interactive, skipped by keyboard |
Radio groups are implicit by DOM adjacency — separators or non-radio items break the group.
Label
A non-interactive heading withrole="presentation".
No props.
Separator
A visual divider withrole="separator". Skipped by keyboard.
No props.
Group
Wraps a submenu trigger + popover pair. No props.DOM Structure
Keyboard Navigation
| Key | Action |
|---|---|
| ArrowDown | Move focus to next menu item |
| ArrowUp | Move focus to previous menu item |
| Home | Move focus to first menu item |
| End | Move focus to last menu item |
| ArrowRight | Open submenu |
| ArrowLeft | Close submenu |
| Enter or Space | Activate focused item |
| Escape | Close menu |
| Tab | Close all menus |
| Letter keys | Typeahead search |
Keyboard opens with focus on first item, mouse opens with focus on trigger (distinguishes via
event.detail).Styling
Menu requires CSS for positioning. The core sets CSS custom properties viagetBoundingClientRect:
For submenu hover safety triangles, style
[data-safe] with a clip-path polygon using the CSS custom properties the core sets (--left, --center, --right, --top, --bottom).Safety Triangle
When a submenu is open, the core creates a triangular safe zone so users can move diagonally to it without accidentally closing it.- The core calculates the submenu’s bounding rect and direction
- Sets
data-safeattribute + CSS custom properties on the Group element - Each
pointermoveupdates the triangle point to follow the cursor - Touch events (
pointerType === "touch") are ignored
Accessibility
- Uses
role="menu"androle="menuitem"for proper semantics - Uses
aria-haspopup="menu"on triggers - Uses
aria-expandedto indicate submenu state - Uses
aria-controlsto link trigger to menu - Uses
aria-labelledbyto associate menu with trigger - Uses
aria-disabled="true"for disabled items (not HTMLdisabledattribute) - Disabled items render as
<span>(not<button>) to prevent click bubbling - Uses roving
tabindex— only one item is focusable at a time
Browser Requirements
Requires Baseline 2024 features:- Popover API — menu positioning
Preventing Menu Close
Calle.stopPropagation() on an item’s click handler to prevent the menu from closing: