An accessible menu pattern built on native popovers with a supporting custom element.
ui-menu keeps the popup surface native and adds the menu behaviors the platform does not provide by itself, including arrow-key navigation, typeahead, and submenu coordination.
import { MenuElement } from 'inclusive-elements';
window.customElements.define('ui-menu', MenuElement);<button type="button" popovertarget="actions-menu" aria-haspopup="menu">
Actions
</button>
<ui-menu id="actions-menu" popover data-placement="bottom-start">
<button type="button" role="menuitem">Edit</button>
<button type="button" role="menuitem">Archive</button>
<button
type="button"
role="menuitem"
popovertarget="more-menu"
aria-haspopup="menu"
>
More
</button>
</ui-menu>
<ui-menu id="more-menu" popover data-placement="right-start">
<button type="button" role="menuitem">Duplicate</button>
<button type="button" role="menuitem">Delete</button>
</ui-menu>- Requires modern browser support for the Popover API.
- The styling examples use CSS anchor positioning.
- Add
popoverto the root<ui-menu>and point a button at it withpopovertargetandaria-haspopup="menu". - Give direct children a menu item role such as
role="menuitem".ui-menuaddsrole="menu"andtabindex="-1"to the menu itself and manages direct menuitem children withtabindex="-1"as well.
- By default, menus are vertical. Arrow Down and Arrow Up move through the enabled items and wrap at the ends.
- Pressing Arrow Down or Arrow Up on the root trigger opens the menu and focuses the first or last enabled item.
- Typeahead matches and moves focus to enabled items by their
aria-labelwhen present, otherwise by their text content.
- To make a horizontal popup menu, set
aria-orientation="horizontal"on<ui-menu>. This keeps the element amenu, not amenubar, and works well for patterns such as reaction pickers. - In a horizontal popup menu, Left and Right move through the enabled items and wrap at the ends.
- To create a submenu, make a menu item that points at another
<ui-menu>withpopovertargetandaria-haspopup="menu". - Submenus open on click, hover, Enter, or Space, Right Arrow in vertical menus, and Down Arrow in horizontal menus.
- Submenus close with Left Arrow in vertical menus and Up Arrow in horizontal menus.
- Clicking a regular menu item closes the open menu tree.
- Escape also closes the open menu tree.
- Tab closes the open menu tree before normal tab order continues.
// Time in milliseconds before the accumulated typeahead search resets.
MenuElement.searchDelay = 800;ui-menu is unstyled. Position the popover surface with native anchor positioning:
[popover] {
position: fixed;
position-anchor: auto;
}
[popover][data-placement='bottom-start'] {
position-area: block-end span-inline-start;
}
[popover][data-placement='right-start'] {
position-area: inline-end span-block-start;
}