mica
mica

Example

Command palette docsNative popoversDialogs

Try the full interactive example for right-click, Shift+F10, optional icons, submenus, and an action that opens a dialog.

Usage

<script type="module" src="menu.js"></script>
<button popovertarget="actions">More</button>
<m-menu id="actions" popover align="end" aria-label="File actions">
  <button type="button">Open file</button>
  <button popovertarget="people">
    <span>Reassign</span><span aria-hidden="true"></span>
  </button>
  <m-menu id="people" popover aria-label="Reassign to">
    <button data-menu-back>← Back</button>
    <button type="button">Alex Morgan</button>
  </m-menu>
  <hr>
  <button disabled>Download original</button>
</m-menu>

Menu items are direct child buttons or links. Native popovertarget connects triggers; the submenu is a direct child of its parent menu. Give each menu an ID and accessible label. Keep actions in application click handlers; Mica does not generate content or dispatch a separate action event.

Context invocation

<article tabindex="0" data-context-menu="actions" aria-label="Project file"><button popovertarget="actions">More</button>
</article>

data-context-menu adds right-click, touch long press, the Context Menu key, and Shift+F10 invocation on a focusable surface. Hold for 500ms to open near the finger. Movement beyond 10px on either axis, scrolling, a second finger, or early release cancels the hold. Enhanced context surfaces disable text selection and the iOS touch callout ahead of the gesture. Removing the enhancement restores ordinary behavior. Use a separate More button when the content needs text selection. The menu sits just beyond a 10px touch anchor, away from the finger. Lifting after activation does not choose an action; tap an item separately. Form controls, links, buttons, and editable descendants keep their normal touch behavior. Keep a normal More button for discoverable access. Avoid binding a broad container that includes editable fields or text-selection areas whose native context menus users need. Context actions may share a menu; your application supplies the correct data.

Content and styling

Icons and trailing shortcut hints are authored SVG and kbd content. Wrap the label in a span to push a trailing hint or chevron to the edge. Shortcut hints do not register hotkeys. Optional hr elements separate actions, and data-menu-heading marks a decorative caption. Use the menu’s accessible label to convey its context. Destructive actions can use data-variant="danger".

align="start|end" sets root button alignment (default start), with RTL support and viewport collision handling. Context invocation positions near the pointer. Override --menu-width (14rem), --menu-gap, or --menu-active-background in your own CSS.

Keyboard and dismissal

Pointer and touch activation open without highlighting an item. Keyboard activation focuses the first item; Arrow Up on the trigger opens at the last item. Arrow Up/Down, Home/End, and typeahead move focus. Enter or Space activates an action. Right Arrow opens a submenu, Left Arrow returns (reversed in RTL). Escape closes one level and restores its invoker; Tab closes the whole menu and continues from the root invoker. Outside presses dismiss the enhanced menu. The module uses manual popovers to prevent the activating touch release from dismissing them; without JavaScript, authored popovers retain native dismissal. Disabled actions remain focusable for discovery but cannot activate: author native disabled buttons or aria-disabled="true" links. Enhancement converts disabled buttons to focusable ARIA-disabled items; update aria-disabled when changing enhanced availability.

At narrow viewport widths, a submenu occupies its parent’s space. Include a data-menu-back button in each submenu; it appears only in the enhanced mobile layout. Unlike a command palette, the menu has no search field and focus moves between actions.

Progressive enhancement

Without menu.js, authored popovers open with their native buttons and use ordinary Tab navigation. Positioning uses the existing popover fallback. Add native close commands to items if they must dismiss without enhancement. With the module, actions close the menu before application handlers run, so handlers can open a dialog safely.

DOM changes are observed; call menu.refresh() for synchronous reconciliation. Removing the element cleans up its listeners and restores the authored attributes. Existing class="menu" popovers remain available for simple tabbed actions, and header navigation menus remain a separate pattern.

← command palette