Vanilla UI Kit Menu v1.0.0 · toast →

Menus, one file.

Zero-dependency dropdown action menus with the full menu-button keyboard pattern — arrows, typeahead, submenus, context menus. Try the keyboard: focus a trigger and press ArrowDown.

<script src="https://cdn.jsdelivr.net/gh/vanilla-ui-kit/components/menu/menu.js"></script>

Basic actions

A trigger button and a list of actions. Esc closes, outside click closes.

new Menu('#m-basic', {
  items: [
    { label: 'Rename',    onSelect: fn },
    { label: 'Duplicate', onSelect: fn },
    { label: 'Move to…',  onSelect: fn }
  ]
})

Icons, hints, danger, disabled

Trusted SVG icons, right-aligned hints, red destructive actions; disabled items are skipped by the arrow keys.

{ label: 'Copy', icon: svg, hint: '⌘C' }
{ label: 'Paste', disabled: true }
{ label: 'Delete', danger: true,
  hint: '⌫', onSelect: fn }

Separators

{ type: 'separator' } groups related actions.

items: [
  { label: 'New file' },
  { label: 'New window' },
  { type: 'separator' },
  { label: 'Close' }
]

Submenu

One level of flyout — hover or ArrowRight to open, ArrowLeft to come back.

{ label: 'Share',
  items: [
    { label: 'Copy link' },
    { label: 'Email' },
    { label: 'Embed' }
  ] }

Context menu

Right-click (or long-press) the area — Menu.open(x, y, items) flips inward at viewport edges.

Right-click here
el.addEventListener('contextmenu', e => {
  e.preventDefault()
  Menu.open(e.clientX, e.clientY, items)
})

Declarative

data-vmn points at a template; no JS needed. Selections fire a menu:select event.

<button data-vmn="#tpl-view">View</button>
<template id="tpl-view">
  <button>Zoom in</button>
  <hr>
  <button data-hint="⌘0">Reset zoom</button>
</template>