vaiperui

components / menu

Menu

Dropdown menu button with arrow-key navigation, typeahead, icons, separators and danger items.

How to use

The list is `role="menu"` with `menuitem` buttons. Arrows, Home and End move between items (disabled ones are skipped), typing a letter jumps to a match, Enter/Space selects, Escape closes and returns focus to the trigger. `onSelect` runs on click and keyboard; call `event.preventDefault()` there to keep the menu open. Compose it with `MenuItem`, `MenuLabel` and `MenuSeparator`.

preview
Menu.tsx
import { Button, Menu, MenuItem, MenuLabel, MenuSeparator } from "vaiperui";
<Menu trigger={<Button variant="outlined">Actions</Button>} label="Actions">
<MenuLabel>Project</MenuLabel>
<MenuItem icon="✎" onSelect={() => rename()}>Rename</MenuItem>
<MenuItem disabled>Archive</MenuItem>
<MenuSeparator />
<MenuItem destructive icon="✕">Delete</MenuItem>
</Menu>

Alignment

preview
start
center
end
alignment.tsx
<Menu align="start" trigger={<Button>start</Button>}>...</Menu>
<Menu align="center" trigger={<Button>center</Button>}>...</Menu>
<Menu align="end" trigger={<Button>end</Button>}>...</Menu>

Props

PropValuesDefaultDescription
triggerReactElementnoneButton that opens the menu (needs ref, focus and onClick)
labelstringnoneAccessible name of the menu
side / aligntop | right | bottom | left / start | center | endbottom / startSide of the trigger and alignment
open / defaultOpen / onOpenChangeboolean / boolean / (open) => voidnoneControlled or uncontrolled open state
sizesm | md | lgmdText size of the items
radius / animationnone | sm | md | lg | full / none | smooth | fast | bouncelg / fastCorner radius and enter transition speed
MenuItemonSelect, icon, destructive, disablednoneItem; `onSelect` runs on click/keyboard and the menu closes unless prevented

Accessibility

  • The list is `role="menu"` named by `label`; each `MenuItem` is a `button` with `role="menuitem"`.
  • The trigger gets `aria-haspopup="menu"`, `aria-expanded` and, while open, `aria-controls`.
  • On open, focus moves to the first enabled item; disabled items are skipped by the arrow keys.
  • Arrow navigation wraps around, and typing letters within 500ms jumps to the next item whose text starts with them.
  • Selecting an item closes the menu and returns focus to the trigger, unless `onSelect` calls `event.preventDefault()`.
  • `MenuSeparator` is `role="separator"`, `MenuLabel` is `role="presentation"` and item icons are `aria-hidden`.

Keyboard

KeyAction
ArrowDownOn the closed trigger, opens the menu; inside, moves to the next item
ArrowUpMoves to the previous item
Home / EndMoves to the first / last item
A-ZTypeahead: jumps to the item whose text starts with the typed letters
Enter / SpaceActivates the focused item
EscCloses the menu and focuses the trigger
TabCloses the menu and lets focus move on

Keep in mind

  • Menu items must be plain actions; this is not a menu bar and there are no checkable (`menuitemcheckbox`/`menuitemradio`) or nested submenu items.
  • Pass `label` so the menu has an accessible name; the trigger needs a visible text or its own `aria-label`.