HTML Guides for menubar
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 <li> inside an element with role="menu" or role="menubar" has a role that doesn't belong in a menu.
These two roles describe an application menu, like the one at the top of a desktop program. Screen readers expect every item in it to be a menuitem, menuitemcheckbox or menuitemradio, sometimes grouped with group or split with separator. An <li> set to listitem, option, link, button or tab breaks that structure. The check covers every <li> under the menu, not only its direct children, so a nested list such as a language picker built as a listbox triggers it too.
An <li> with no role attribute passes, and so does one with role="none" or role="presentation". Only an explicit role outside the allowed list raises the error.
Usually the menu roles shouldn't be there at all. They tend to end up on a site's main navigation, which is just a list of links to other pages. A screen reader that announces a "menu bar" tells the user to move between items with the arrow keys, and few sites script that behavior. Remove role="menu", role="menubar" and the item roles, and keep a <nav> with a plain list of links. Save the menu roles for widgets that work like a desktop menu, where each item runs a command. There, each <li> is either a menu item itself or has role="none" and wraps one.
Invalid example
A language picker built as a listbox sits inside navigation marked up as a menubar. Each <li role="option"> triggers the error:
<nav aria-label="Main">
<ul role="menubar">
<li role="none"><a role="menuitem" href="/">Home</a></li>
<li role="none"><a role="menuitem" href="/news/">News</a></li>
<li role="none">
<ul role="listbox" aria-label="Language">
<li role="option" aria-selected="true">English</li>
<li role="option" aria-selected="false">Español</li>
</ul>
</li>
</ul>
</nav>
Valid example
Site navigation as plain lists of links, with no menu roles:
<nav aria-label="Main">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/news/">News</a></li>
</ul>
<ul aria-label="Language">
<li><a href="/" aria-current="true">English</a></li>
<li><a href="/es/" hreflang="es" lang="es">Español</a></li>
</ul>
</nav>
For a real application menu, every <li> gets a menu role or role="none". The items also need scripted keyboard support (arrow keys, Escape, focus handling), as the WAI-ARIA menubar pattern describes:
<ul role="menubar" aria-label="Editor">
<li role="none">
<button type="button" role="menuitem" aria-haspopup="menu" aria-expanded="true">Format</button>
<ul role="menu" aria-label="Format">
<li role="menuitemcheckbox" aria-checked="true" tabindex="-1">Bold</li>
<li role="menuitemcheckbox" aria-checked="false" tabindex="-1">Italic</li>
<li role="separator"></li>
<li role="menuitem" tabindex="-1">Clear formatting</li>
</ul>
</li>
</ul>
The aria-haspopup attribute belongs on the menu item that opens a submenu, not on the role="menubar" element that holds the items.
aria-haspopup tells screen readers that activating an element opens a popup, such as a menu or a dialog. A menu bar doesn't open anything. It stays on screen, and users activate the items inside it. WAI-ARIA 1.2 deprecated aria-haspopup as a global property and lists the roles that support it: application, button, combobox, gridcell, link, menuitem, slider, tab, textbox and treeitem, plus the roles that inherit from them. menubar isn't on that list, so assistive technology can ignore the attribute there, and users can't tell which item has a submenu.
The validator flags any element with an explicit role="menubar" and an aria-haspopup attribute, whatever its value, so aria-haspopup="false" triggers the warning too. If the element is a real menu bar, move aria-haspopup="menu" to each menuitem that opens a submenu, and add aria-expanded there to say whether that submenu is open.
Sometimes the element isn't a menu bar at all. It's a control that shows a dropdown, like a language picker or a mobile navigation toggle. Drop role="menubar" there and use a <button> with aria-expanded and aria-controls, as the disclosure pattern describes. The links inside the panel stay plain links.
Invalid example
<ul role="menubar" aria-label="Editor" aria-haspopup="menu">
<li role="none">
<button type="button" role="menuitem" aria-expanded="false" aria-controls="file-menu">File</button>
<ul role="menu" id="file-menu" aria-label="File" hidden>
<li role="menuitem" tabindex="-1">New</li>
<li role="menuitem" tabindex="-1">Open</li>
</ul>
</li>
<li role="none">
<button type="button" role="menuitem" tabindex="-1">Help</button>
</li>
</ul>
Valid example
The menu item that opens the submenu carries aria-haspopup:
<ul role="menubar" aria-label="Editor">
<li role="none">
<button type="button" role="menuitem" aria-haspopup="menu" aria-expanded="false" aria-controls="file-menu">File</button>
<ul role="menu" id="file-menu" aria-label="File" hidden>
<li role="menuitem" tabindex="-1">New</li>
<li role="menuitem" tabindex="-1">Open</li>
</ul>
</li>
<li role="none">
<button type="button" role="menuitem" tabindex="-1">Help</button>
</li>
</ul>
A dropdown of links, such as a language picker, uses a disclosure button instead of menu roles:
<button type="button" aria-expanded="false" aria-controls="language-list">Language</button>
<ul id="language-list" hidden>
<li><a href="/" hreflang="en">English</a></li>
<li><a href="/es/" hreflang="es" lang="es">Español</a></li>
</ul>
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