HTML Guides for menu
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The WAI-ARIA specification defines strict ownership requirements for certain roles. The menuitem role represents an option in a set of choices and is only meaningful when it exists within the context of a menu. When a menuitem appears outside of a menu or menubar, screen readers and other assistive technologies have no way to determine that it belongs to a menu widget. They cannot announce the total number of items, provide keyboard navigation between items, or convey the menu's hierarchical structure to the user.
This requirement follows the concept of required owned elements and required context roles in ARIA. Just as a <li> element belongs inside a <ul> or <ol>, a menuitem belongs inside a menu or menubar. The relationship can be established in two ways:
- DOM nesting — the
menuitemelement is a DOM descendant of themenuormenubarelement. - The
aria-ownsattribute — themenuormenubarelement usesaria-ownsto reference themenuitemby itsid, establishing ownership even when the elements aren't nested in the DOM.
It's important to note that ARIA menu roles are intended for application-style menus — the kind you'd find in a desktop application (e.g., File, Edit, View menus). They are not meant for standard website navigation. For typical site navigation, use semantic HTML elements like <nav> with <ul>, <li>, and <a> elements instead.
How to fix it
- Identify every element with
role="menuitem"in your markup. - Ensure each one is contained within an element that has
role="menu"orrole="menubar", either through DOM nesting or viaaria-owns. - Choose the correct parent role:
- Use
role="menubar"for a persistent, typically horizontal menu bar (like a desktop application's top-level menu). - Use
role="menu"for a popup or dropdown menu that contains a group of menu items.
- Use
- If you're using menus for site navigation, consider removing the ARIA menu roles entirely and using semantic HTML (
<nav>,<ul>,<li>,<a>) instead.
Examples
Incorrect — menuitem without a menu context
This triggers the validator error because the menuitem elements have no parent menu or menubar:
<div>
<div role="menuitem">Cut</div>
<div role="menuitem">Copy</div>
<div role="menuitem">Paste</div>
</div>
Correct — menuitem inside a menu
Wrapping the items in an element with role="menu" resolves the issue:
<div role="menu">
<div role="menuitem" tabindex="0">Cut</div>
<div role="menuitem" tabindex="-1">Copy</div>
<div role="menuitem" tabindex="-1">Paste</div>
</div>
Correct — menuitem inside a menubar
For a persistent horizontal menu bar with application-style actions:
<div role="menubar">
<div role="menuitem" tabindex="0">File</div>
<div role="menuitem" tabindex="-1">Edit</div>
<div role="menuitem" tabindex="-1">View</div>
</div>
Correct — nested menus with dropdown submenus
A menubar with a dropdown menu containing additional menuitem elements:
<div role="menubar">
<div role="menuitem" tabindex="0" aria-haspopup="true" aria-expanded="false">
File
<div role="menu">
<div role="menuitem" tabindex="-1">New</div>
<div role="menuitem" tabindex="-1">Open</div>
<div role="menuitem" tabindex="-1">Save</div>
</div>
</div>
<div role="menuitem" tabindex="-1" aria-haspopup="true" aria-expanded="false">
Edit
<div role="menu">
<div role="menuitem" tabindex="-1">Cut</div>
<div role="menuitem" tabindex="-1">Copy</div>
<div role="menuitem" tabindex="-1">Paste</div>
</div>
</div>
</div>
Correct — using aria-owns for ownership without DOM nesting
When the menuitem elements cannot be nested inside the menu in the DOM (e.g., due to layout constraints), use aria-owns to establish the relationship:
<div role="menu" aria-owns="item-cut item-copy item-paste"></div>
<div role="menuitem" id="item-cut" tabindex="0">Cut</div>
<div role="menuitem" id="item-copy" tabindex="-1">Copy</div>
<div role="menuitem" id="item-paste" tabindex="-1">Paste</div>
Better alternative — use semantic HTML for site navigation
If you're building standard website navigation (not an application-style menu), avoid ARIA menu roles altogether:
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
This approach is simpler, more accessible by default, and doesn't trigger the validator warning. Reserve role="menu", role="menubar", and role="menuitem" for true application-style menus that implement full keyboard interaction patterns as described in the ARIA Authoring Practices Guide.
An element with an interactive ARIA role, such as menuitem or button, is nested inside another element whose role already describes a single control, like a menu item inside a menu item.
ARIA in HTML treats an element with one of these outer roles like a native <button>: it can hold phrasing content, but no interactive descendants. Most of them also have presentational children in WAI-ARIA, so browsers don't expose their descendants to assistive technologies as separate elements. A screen reader announces the outer menu item and reads anything nested inside it as plain text. The arrow-key navigation of a menu can't reach a nested item either.
The validator checks descendants at any depth inside elements with role set to button, checkbox, img, link, math, menuitem, menuitemcheckbox, menuitemradio, option, progressbar, radio, separator, slider, switch or tab. It flags descendants whose role is button, checkbox, combobox, grid, gridcell, listbox, menu, menubar, menuitem, menuitemcheckbox, menuitemradio, option, radio, scrollbar, searchbox, slider, spinbutton, switch, tab, textbox or treeitem.
Nested submenus are the most common cause. A site menu gives each <li> role="menuitem" and puts the submenu, a <ul role="menu"> with its own menu items, inside that <li>. Each submenu then gets one warning for the role="menu" list and one for every item in it. If the <li> elements also hold links, those links get their own warnings, starting with "The element "a" should not appear as a descendant of an element with the attribute "role=menuitem"." The same fix clears all of them.
In the WAI-ARIA menu pattern, a submenu sits next to the item that opens it, not inside it. Give each <li> role="none", put role="menuitem" on the link or button inside it, and place the submenu <ul role="menu"> after that element in the same <li>. Add aria-haspopup="true" and aria-expanded to the parent item so screen readers know it opens a submenu.
If the list is ordinary site navigation, remove role="menubar", role="menu" and role="menuitem" altogether. The menu roles are meant for application menus like the ones in desktop software, and they need scripts that move focus with the arrow keys. A <nav> with nested lists of links and a <button aria-expanded> for each submenu needs no ARIA roles.
Other role combinations have the same fix: keep one interactive role per control. Move the role onto the element that performs the action, or move the inner control out of the outer one.
Invalid example
<ul role="menubar" aria-label="Main">
<li role="menuitem" aria-haspopup="true" aria-expanded="false">
Services
<ul role="menu" aria-label="Services">
<li role="menuitem">Consulting</li>
<li role="menuitem">Training</li>
</ul>
</li>
</ul>
Valid example
<ul role="menubar" aria-label="Main">
<li role="none">
<button type="button" role="menuitem" aria-haspopup="true" aria-expanded="false">Services</button>
<ul role="menu" aria-label="Services">
<li role="menuitem">Consulting</li>
<li role="menuitem">Training</li>
</ul>
</li>
</ul>
Valid example for site navigation
<nav aria-label="Main">
<ul>
<li>
<button type="button" aria-expanded="false" aria-controls="services-menu">Services</button>
<ul id="services-menu" hidden>
<li><a href="/consulting">Consulting</a></li>
<li><a href="/training">Training</a></li>
</ul>
</li>
</ul>
</nav>
An <li> element inside a <ul>, <ol>, or <menu> must not be assigned any ARIA role other than listitem.
When an <li> sits inside a standard list container (<ul>, <ol>, or <menu>) that has no explicit role attribute, the browser already treats the <li> as having an implicit role of listitem. Assigning a different role, such as role="presentation", role="option", or role="tab", contradicts the semantics of the parent-child relationship between the list and its items. Screen readers and other assistive technologies rely on this relationship to convey list structure to users.
The same restriction applies when the parent element has role="list" set explicitly. In that context, child <li> elements are still expected to carry only the listitem role.
If you need a different role on the items, restructure the markup. For example, use <div> elements instead of <ul> and <li>, and apply the appropriate roles directly. Or, if the items genuinely are list items, remove the conflicting role attribute.
Examples
Invalid: li with a non-listitem role inside a ul
<ul>
<li role="tab">Dashboard</li>
<li role="tab">Settings</li>
<li role="tab">Profile</li>
</ul>
Fixed: use appropriate elements when a different role is needed
If the items are tabs, drop the list markup and use elements that match the intended semantics:
<div role="tablist">
<button role="tab" aria-selected="true">Dashboard</button>
<button role="tab" aria-selected="false">Settings</button>
<button role="tab" aria-selected="false">Profile</button>
</div>
Fixed: keep the list but remove the conflicting role
If the content really is a list, remove the extra role:
<ul>
<li>Dashboard</li>
<li>Settings</li>
<li>Profile</li>
</ul>
The type attribute on the <menu> element is obsolete and should be removed.
The <menu> element was originally designed to support different types of menus, including type="context" for context menus and type="toolbar" for toolbars. These features were never widely implemented by browsers and have been removed from the HTML specification.
In the current HTML living standard, the <menu> element is simply a semantic alternative to <ul> for representing a list of interactive items or commands, such as a toolbar of buttons. It no longer accepts a type attribute.
If you need a custom context menu (right-click menu), the recommended approach is to use JavaScript to listen for the contextmenu event and display your own custom menu using standard HTML and CSS.
Invalid Example
<menu type="context" id="my-menu">
<menuitem label="Copy"></menuitem>
<menuitem label="Paste"></menuitem>
</menu>
Valid Example
Using <menu> as a simple list of commands:
<menu>
<li><button>Copy</button></li>
<li><button>Paste</button></li>
</menu>
If you need a custom context menu, handle it with JavaScript:
<div id="target">Right-click here</div>
<menu id="context-menu" style="display: none; position: absolute;">
<li><button>Copy</button></li>
<li><button>Paste</button></li>
</menu>
<script>
const target = document.getElementById("target");
const menu = document.getElementById("context-menu");
target.addEventListener("contextmenu", (e) => {
e.preventDefault();
menu.style.display = "block";
menu.style.left = e.pageX + "px";
menu.style.top = e.pageY + "px";
});
document.addEventListener("click", () => {
menu.style.display = "none";
});
</script>
Note that the <menuitem> element is also obsolete and no longer part of the HTML specification. Use standard elements like <button> or <a> inside <li> elements instead.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries