Skip to content
Open UI

Menu Elements (Explainer)

Table of Contents

Introduction

A menu is an essential way to group menu items together and provide users the ability to execute commands. There is an existing <menu> element, which represents a “toolbar” listing of commands. Though it is named “menu”, it is not programmatically exposed like a “menu” or “menubar”. There is also no native support to make listed commands look and behave cohesively. This proposal introduces new menu elements to give web developers more flexibility while following the ARIA practices on menu and menubar. Namely, our proposal includes adding <menubar>, <menulist>, and <menuitem> elements.

Having native menu features will make it easier for web developers to add menulists, menubars and better configure the menu items to choose from. Furthermore, introducing these new features will expand on the baseline behaviors popover and anchor positioning provide. New native menu elements will also have the correct implicit ARIA roles, states and properties as well as the expected keyboard navigation behaviors - ensuring consistent experiences for users on the platform they are engaging with.

Use cases:

  • Add an in-page horizontal menubar at the top of a content window; example: Google Doc has a menubar with the items File, Edit, View, Insert, Format, etc.
  • Add a filter button that opens a menulist to a shopping catalogue where user can check between “Sort by relevant”, “Sort by newest”, “Sort by price”.

New <menuitem> elements

<menuitem>

  • A focusable/activatable command in a <menubar> and <menulist>.
  • Supports the disabled attribute, and the :disabled CSS pseudo-class.
  • Supports the command/commandfor attributes to specify popover target.
  • Events:
    • “toggle” on popover will be fired.
    • “change” will be fired when the <menuitem> is checkable, and its state changes
  • Checkability:
    • Supports “checkable” configuration and state, by wrapping <menuitem>s in a <fieldset> with a new checkable IDL attribute (with no corresponding content attribute); see Grouping menuitems together.
    • Supports the ability to set initial checkedness in HTML, via the defaultchecked content attribute (reflect by the defaultChecked IDL attribute).
    • Supports the :checked CSS pseudo-class and ::checkmark CSS pseudo-element

Question: Should <menuitem>s be represented by a single element, or multiple elements?

As stated, our proposal involves introducing a single <menuitem> element to represent menu buttons, some of which can be checkable. Instead of representing both non-checkable & checkable state-bearing menu items with the single <menuitem> element, we could also consider:

  • Add a type attribute: <menuitem>, <menuitem type=checkbox>, <menuitem type=radio>.
    • If type is checkbox or radio, it is automatically checkable
  • Add a checkable IDL attribute on <menuitem>
    • This IDL property could be used to see if the item is checked
    • Use a name attribute to group exclusive menu items together (this only applies for “radio” menu items):
      • <menuitem checkable name=foo>
      • <menuitem checkable name=foo>
  • Add a checkable IDL attribute on the grouping element (<fieldset>, <menulist>)
    • This attribute can have the values single (for radios) or multiple (for checkbox)

Question: Should we allow radio and checkbox menu items as direct children of a <menubar>?

Currently, we don’t see a need for this. All menuitems inside a menubar should perform some action like opening a sub <menulist>, however we will reconsider this restriction if good use cases arise.

For example, a text editor might need to provide a <menulist> to provide basic font style adjustments. A group of checkable <menuitem> elements to mark content as “Bold”, “Italic” or “Underlined”, could be provided. The checked state representing whether the text is currently “Bold” (checked) or not.

Grouping <menuitem>s together

Our proposal involves grouping <menuitem> elements together with the new <menubar> and <menulist> elements, and does not modify the existing <menu> element processing model. See https://github.com/openui/open-ui/issues/1194 for more discussion about this.

<menubar>

  • Menubar should be used to represent a series of <menuitem>s that can then open submenus of commands.
  • In a menubar, a menuitem’s most common purpose is to invoke submenus (menulists).
  • An in-page menu that can contain a list of <menuitem>.
  • Menuitems within a menubar are displayed horizontally by default.
  • Supports :disabled, disabled attribute.

<menulist>

  • Menulist should be used to represent submenus of commands.
  • In a menulist, a menu item’s most common purpose is to invoke actions (commands) or other submenus (menulists).
  • Menulist is a popover menu that can contain a list of <menuitem>s.
  • Anchored to the button which is declaratively set up to open it.
  • Attributes
    • disabled, open, name?

Question: Is <fieldset> necessary to group <menuitem> together?

Yes. While, we can use the “name” attribute to logically group exclusive checkable <menuitem>s together, to support captioned groups of <menulist> items, we should use the existing <fieldset> element (and its optional labeling <legend> element) that give this functionality to forms. If multiple <menuitem>s are added without a <fieldset>, they are grouped together under their parent <menulist> or <menubar>, but are logically separate and are not checkable.

Note, <fieldset> is a more general grouping element than <optgroup>, which we briefly considered, but decided against since it is used for grouping options.

Our proposal includes adding a checkable attribute on <fieldset>, to group <menuitem>s as either radios or checkboxes, instead of introducing new <menuitemcheckbox> and <menuitemradio> elements. We envision the following markup:

<menulist>
  <fieldset checkable=single>
    <legend>Sort by</legend>
    <menuitem>Recently added</menuitem>
    <menuitem>Date created</menuitem>
    <menuitem>Creator</menuitem>
  </fieldset>

  <fieldset checkable=multiple>
    <menuitem>1 bedroom</menuitem>
    <menuitem>2 bedrooms</menuitem>
    <menuitem>3 bedrooms</menuitem>
  </fieldset>
</menulist>

Question: How should we expose the checked menu items to script?

It can be queried together as :checked element.

Question: Should we restrict <menubar> and <menulist> to only contain <menuitem>s?

Similarly to customizable select, we expect everything to be wrapped inside <menuitem> according to the content model. This includes non-interactive elements like logo, images, and text.

We should support <fieldset> and <legend> to help group the menu items together.

We should support specific use cases of interactive elements outside the <menuitem>. For example, if the user wants to add an <input type=search> element, it should not be wrapped inside a <menuitem> and should instead be a sibling of <menuitem>s.

menubar examples with search input as a sibling of menuitems

Invoking a menulist from a button element

While a <menubar> contains a collection of <menuitem> elements that can be used to invoke their own submenu (<menulist> elements), <menubar>s are commonly used for more global or multi-component-level purposes. If an author needs to provide a <menulist> of commands to interact with specific content or components of an interface, then a <button> element is commonly used to invoke a <menulist>.

For instance, <button> elements that are used to reveal menus are commonly used in tables/grids to allow for adjustments to the presentation of the tabular data, if not to invoke the editing or deleting of specific row or cell content.

a button invokes a menulist in a table column header, revealing the menuitems to resize, reorder or sort by the column of which it was invoked.

Toolbars or other groupings of related controls within complex web interfaces may need to include buttons that can perform many actions - the specific action to perform is chosen by the user when they interact with the primary button, invoking its menu of commands.

the youtube player consists of multiple buttons that invoke their own menulists. The settings button invokes a menulist allowing for the adjustment of quality, playback speed, toggling annotations and more. the create new button in github's top bar invokes a menulist to add new or import a repository, create a new codepace or gist, or make a new orginization or project.

Leveraging the command and commandfor attributes, we propose that <button> elements, outside of the context of a <menubar>, can invoke <menulist> elements. A <button> that invokes a <menulist> would have an implicit aria-haspopup=menu property, along with an impliict aria-expanded state, reflecting whether the element is rendered (expanded) or not (collapsed). It would not have an implicit aria-details property. This is because unlike other more generic popovers, keyboard focus should immediately move from the invoking button to the first focusable element, or element with the autofocus attribute, of the rendered <menulist>.

For instance:

<button command=open-menu commandfor=m>Actions</button>
<menulist id=m>
  <!-- menuitems go here -->
</menulist>

General questions

When to use Select vs Menu

Just like a select element, the menu element has a list of choices to pick from. And with customizable select now available, users might be confused on which one to use.

The <select> element is a form-associated element and contains <option> elements. Each of these <option> elements must have a server-readable value. As a user chooses an option, specific events are triggered (change, input) and a new value is set.

On the other hand, the proposed <menulist> element should be used to display a list of commands/actions. Each <menuitem> should be thought of as a command that is activable. That action could be to open a new nested menulist element or to trigger a specific command. It must adhere to ARIA guidelines for menu and menubar.

Why are we adding <menuitem> and not re-using the <command> element?

See <command> element’s deprecated specification here. In the last year, the command and commandfor attributes were introduced so buttons can perform actions on other elements declaratively. We thought re-using this obsolete element with the same name would be confusing to developers.

Additionally - command is a single element, where as this proposal is introducing various elements with additional grouping semantics and keyboard behaviors beyond what a single ‘command’ element would represent - or reasonably be capable of creating parity with the various ARIA roles these new proposed elements would be natively introducing to HTML.

Why are we adding <menubar> and <menulist> and not re-using the <menu> element?

The <menu> element already exists, but it is very barebones. It represents a “toolbar” but is exposed no differently than an unordered list containing list items, each list item then representing a command (represented by a button element) that the user can perform or activate. There is a lot of nuance between the various menu patterns (menubar, navigation menu, toolbar) that this one element cannot differentiate. It has no special keyboard behavior nor unique ARIA roles mapping beyond the implicit roles of the elements it represents. Due to these legacy restrictions, we recommend not modifying it and instead depend on the new elements <menubar> and <menulist>.

You can read more about what the menu element is and isn’t, here.

Popover semantics for <menulist>

Given a <menuitem> inside a <menubar>, it can invoke a <menulist> by invoking a target.

This can be accomplished by either:

  • <menuitem command="show-popover" commandfor=id>
  • <menuitem command="show-menu" commandfor=id>
    • We should allow command invokers.
    • Useful for menu items that invoke custom commands.
    • Useful for interest invoker targets.
    • Useful to show modal dialog.
    • To decide: Does this make sense for radio/checkbox menuitems? Do we have use cases of a menuitem that toggles its checked state and invoke a behavior at the same time?
  • Nest the menulist as a sibling of other menuitems, inside the parent menulist.

Questions

  • Should <menulist> be a popover by default?
    • Yes, the popover attribute is not necessary. A menulist is by default a popover with value auto.
  • Since <menulist>s are popovers by default, what happens if we add a popover attribute on top of it?
    • It can be specified to overwrite the popover=auto default and to set to popover=hint or popover=manual.
  • Should menulist.showPopover() be the way to open these from script?
    • Yes (for now).

Why are we using popover instead of openable for <menulist>?

An ”openable” is an element rendered in-page with display:none until it is opened. This could be useful for mobile apps, where the menubar is shown vertically in-page.

An element with role=menu is most commonly rendered as a popover. If someone wanted an in-page menu, then a menubar or fieldset of controls may be more appropriate. Though, if one wanted to show/hide a grouping of <menuitem> elements within a <menulist> popover, rather than as an adjacent submenu popover, then the following could be done:

<menulist>
  ...
  <menuitem command="toggle-openable" commandfor="o">Colors</menuitem>
  <fieldset aria-label=colors openable id=o>
    <!-- grouped menuitems go here -->
  </fieldset>
</menulist>

Someone could show/hide a menubar - much like how Google docs allows one to show/hide the menubar with the toggle on the right side of the toolbar (hide the menus (ctrl+shift+f) button).

Openable is currently not standard. If there are valid cases of needing the <menulist> element to be in-page and the feature is mature enough, we can revisit this.

What should be the keyboard behavior?

We can use the same keyboard behavior as described in the table of the ARIA Authoring Practices Guide, which is demonstrative of the keyboard expectations for how menus and menubars work at the operating system level.

  • A menubar has a single tab stop.
  • A menulist is invoked when its popovertarget (a menuitem or a button). Focus would automatically move to its first focusable child.
  • Tab focus will not trigger the menuitem to get selected/checked, but only when an explicit Enter/choice happens.
  • An explicit Enter/Space on a menuitem without popvertarget and inside a menulist will close that menulist.
  • An explicit Enter/Space does not close the menulist if the menuitem selected has a popovertarget that gets triggered.
  • An explicit ESC will close the current popover menulist that has focus and return focus to its invoking element (which may be a menuitem inside a menulist or a menubar; or is a button element).
  • Arrow keyboard events will allow navigating between menuitems.

Questions

  • Is there any case where tab will close a menulist? For example, when we move from the last <menuitem> in a <menulist> and are moving to the next <menuitem> in the <menubar>.
  • Do we need special behavior for hot keys?

Should clicking/activating a menuitem close the menu by default?

When user clicks on a menuitem:

  1. If it has a valid reference to a menulist:
    1. If menulist is open, close the menulist (and all its nested menulist children).
    2. Else, open it and move focus.
  2. Else, if menuitem is contained inside an open menulist, close its parentMenulist.

The algorithm when the user activates a <menuitem> in a <fieldset checkable=multiple>:

  1. Set it to checked.

The algorithm when the user activates a <menuitem> in a <fieldset checkable=single>:

  1. Set it to checked.
  2. Set all other radio menu items in the same group to unchecked.

Questions

  • Should there be an attribute to control this behavior?
  • Should “focusout” on a <menulist> cause it to close?
  • What about hovering on <menuitem>? This is not supported for popovers yet.
  • Define the click and drag behavior.
  • Define the activation behavior.
  • Define the behavior for nested list cases. We can leverage that menulist are popovers, meaning the focus is scoped already.

Should any of these elements be form-associated?

No. the menu elements proposal is intended to be semantically different from customizable <select>, which is in part achieved by the difference in form-association support. Supporting form-assocation also increases complexity (it raises questions like: what do we do if some of the controls appear outside of the menu element tree? how does form-association integrate with menu nesting?) with no obvious benefit; until a strong use case materializes, our default will be to not form-associate elements in this proposal.

However, we do recognize there are use cases where users might want to have inputs inside the menulists or menubars. For example, searchable menus using an input element. In such cases, the control is allowed in the menulist or menubar, but is not associated with a form.

How can users customize the UI of these menu elements with CSS?

We propose these elements should not support the CSS appearance property and just have a “base” appearance by default like the <dialog> element.

Questions

  • <menubar> should list <menuitem>s horizontally, but support using CSS writing-mode.
    • Can have vertical case by:
      • Set CSS writing-mode: vertical-lr on the <menubar>
      • Set CSS writing-mode: horizontal-tb on the <menuitem> inside the <menubar>
  • <menulist> can support more than top to bottom layout:
    • Set CSS display: grid and use rows/columns.
  • For checkable <menuitem>, should the check mark be on the left or the right?
    • Default should be on the left.
    • This can depend on the CSS direction property.
  • For <menuitem>, should there be a default slot for the icon? If so, should it be on the left or the right?
    • Default should be on the left.
    • This can depend on the CSS direction property.

There was a proposal for a Context Menu years ago. How is this different?

Previously, “context menus” were specified to allow developers to define custom context menus declaratively. This was being prototyped by Mozilla. Blink also had an implementation behind a flag.

<menu id="menu1" type="context">
  <a href=#>Cancel</a>
</menu>

<button contextmenu="menu1">Right-click me</button>

Per comment and PR, this feature was removed from the HTML specification because there was a lack of active interest by at least two implementers.

From Chromium, the reasoning for removing support was:

  1. There is doubt whether the API is well designed.
  2. This was considered a low priority because “reaching outside the content area and contributing items to the browser’s context menu” is a new capability and needs thorough designing. There was no bandwidth for new capabilities work.
  3. This is not an interoperability priority (only existed in Firefox)

Chromium bug: https://issues.chromium.org/issues/40589971

Mozilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=617528

Mozilla bug to remove support: https://bugzilla.mozilla.org/show_bug.cgi?id=1372276

Webkit bug about the touch bar API: https://bugs.webkit.org/show_bug.cgi?id=179020

The specification were removed per:

https://github.com/whatwg/html/pull/2742

https://github.com/whatwg/html/pull/2342

https://github.com/whatwg/html/pull/244

How can we use menu elements to support custom right click Context Menus?

We discourage users from using these new elements to build a custom context menu. There are good reasons to keep the user agent defined context menu. For example, it allows users to copy/paste consistently across sites and to have browser extension options added. We do not plan to provide a declarative way to build a context menu.

However, this proposal is not about removing the existing web capabilities. Authors can use Javascript to add an event listener on “contextmenu” and call preventDefault to not show the default context menu. They can support custom context menus (similar to Google Doc) by showing a <menulist> at the cursor location.

We understand it would be helpful to have this feature be natively supported. If we want to open the conversation about supporting this, here are some open questions to resolve:

  • What should be the keyboard behavior?
  • What are the accessibility mappings? See open ARIA issue.
  • Can we add support to add individual <menuitem> to the existing UA context menu?
  • How can we keep the OS context menu’s style?
  • Before we can support this, we need to fix this popover bug about values auto vs manual.

How can we use menu elements to support navigation menus?

A “navigation menu” represents a means to allow users to access different pages/screen or sub-pages/screens of a website or web application - commonly used in many blogs, SPAs, news websites, really anywhere. The most common way to create a navigation menu is by using a <nav> element, which implicitly exposes the ARIA landmark navigation role. Lists, hyperlinks and buttons (to show/hide sub-navigation items) are the commonly expected child elements of many standard navigations, which generally do not need an author to declare specific ARIA roles to be accessible. Since all items in standard navigations are commonly hyperlinks and buttons, users can rely on standard Tab key navigation to reach each interactive element of the navigation.

Sometimes, to reduce the amount of keyboard tabbing to bypass large navigation menus, a Disclosure Navigation Menu pattern or Menubar Navigation pattern may be used. Each have their own pros and cons.

We are evaluating the possibility to include Navigation Menubar pattern in our proposal. We need to carefully evaluate if this is a valuable addition, as there are long standing UX concerns with using menubars in this way, and if such a pattern is introduced, we want to make sure it addresses the concerns of all users.

Questions

  • Should we reuse the <nav> element if we were to support a “disclosure” or “menubar” navigation pattern?
  • Instead of using <menubar>, should we have a new <navmenubar> element?
  • Should we support anchor elements inside <menuitem>s?
    • No, <menuitem> should only wrap non-interactive elements.
  • Should we support href attribute on <menuitem>s?
    • Maybe, if we commit to the “navigation menu” use case.

Questions about VoiceOver on navigation menubar

  1. I tried Voiceover with on the chrome menus themselves and commands affecting the current context like copy/past aren’t obviously distinguishable from commands that are more navigation like (e.g. settings…). Why is that uniquely important for the web?
  2. I tried Voiceover on google docs and tab navigation for the menus doesn’t seem to work very well but when I am able to tab to Help => Privacy Policy (opens a new tab), it’s not announced as a link.

How can we use menu elements to support toolbars?

A menubar provides a hierarchical structure of menus for accessing application functions while a toolbar provides quick access to commonly used commands through icons and buttons. As such, a toolbar can contain a variety of controls beyond just menuitems. For example, buttons and comboboxes to invoke menulists.

There are ARIA guidelines for the toolbar pattern, which is different from the menubar and the navigation menu patterns. This proposal will not address this yet.

How can we use menu elements to support Touch Bar?

There is a history of attempting to do this in Webkit, for example for a Touch Bar. The idea for this is to collect all your “commands” and put them into the macOS menu bar (or expose them some other way). This proposal will not address this yet.

Examples in code

Google Docs: nested menulist

menubar example
<menubar>
 <menuitem menu=format>Format</menuitem>
 <menuitem menu=tools>Tools</menuitem>
 <menuitem menu=extensions>Extensions</menuitem>
 <menuitem menu=help>Help</menuitem>
</menubar>
<menulist id=format>
  <menuitem menu="format-text">Text</menuitem>
  <menuitem menu="format-paragraph-styles">Paragraph styles</menuitem>
  <menuitem menu="format-align-indent">Align & indent</menuitem>
  ...
 <menulist id="format-text" checkable=multiple>
  <menuitem id="bold">Bold</menuitem>
  <menuitem>Italic</menuitem>
  <menuitem>Underline</menuitem>
  ...
 </menulist>
</menulist>

<script>
bold.addEventListener("click", () => {
  let text = document.getElementById("text");
  text.style.fontWeight = 'bold';
});
</script>

Future enhancements

We envision more advanced capabilities in the future that could enhance the usability of the menu elements designed in this explainer, but that are not necessarily in scope for our initial proposal.

One such capability is keyboard shortcuts to invoke <menuitem> commands; see https://github.com/openui/open-ui/issues/1225. We envision that this could be provided by a more advanced version of the accesskey primitive, that would provide customizable key commands that apply outside the scope of our proposal.

Reading List

Menus have been heavily discussed, please see below to learn about the existing specification and efforts.

ARIA guidelines

Relevant ARIA issues

Relevant Navigation specific issues

APG pattern recommendations (note these are not guidelines)

HTML specification

HTML specification for previous efforts

Blog posts from accessibility community

Blog posts for Design System

Issues / Discussions

This section links to all of the relevant discussions and issues related to menu:

OpenUI