Skip to content

Menu Button

The ARIA menu-button pattern. Selecting an item dispatches a re-select event; full keyboard support is added by enhanceMenuButton. Prefer a self-managing tag? The <re-menu> custom element wraps this same pattern.

Live example
<div class="re-menu" data-re-menu id="mb-1">
  <button
    type="button"
    class="re-button"
    data-variant="secondary"
    aria-haspopup="menu"
    aria-expanded="false"
    aria-controls="mb-1-panel"
    id="mb-1-btn"
  >
    Actions
  </button>
  <div class="re-menu__panel" role="menu" id="mb-1-panel" aria-labelledby="mb-1-btn" hidden>
    <button class="re-menu__item" role="menuitem" data-value="rename" id="mi-rename">
      Rename
    </button>
    <button class="re-menu__item" role="menuitem" data-value="duplicate" id="mi-duplicate">
      Duplicate
    </button>
    <div class="re-menu__separator" role="separator"></div>
    <button class="re-menu__item" role="menuitem" data-value="delete" id="mi-delete">
      Delete
    </button>
  </div>
</div>
  • Keyboard — On the trigger: ArrowDown, Enter, or Space opens the menu and moves focus to the first item; ArrowUp opens it and moves focus to the last item; clicking toggles it. Within the open menu: ArrowDown/ArrowUp move between items (wrapping at the ends), Home/End jump to the first/last item, Enter/Space activate the focused item, and Escape closes the menu and returns focus to the trigger. Tab closes the menu rather than trapping focus.
  • Focus — The trigger and items are native <button>s, so they show the standard :focus-visible ring. Menu items draw an inset ring so it stays visible against the panel edge. Closing via Escape or selecting an item returns focus to the trigger.
  • Semantics — The trigger carries aria-haspopup="menu", aria-controls pointing at the panel, and aria-expanded, which enhanceMenuButton toggles between true and false so assistive tech announces the open/closed state. The panel is role="menu" labelled by the trigger via aria-labelledby; each item is role="menuitem" and dividers are role="separator".
  • Notes — Disabled items (disabled or aria-disabled="true") are skipped by keyboard navigation and excluded from selection. See the accessibility guide.