Skip to Content
Component ExamplesMega Menus

Mega Menus

Large dropdown navigation panels — using the disclosure pattern, NOT ARIA menu roles.

Maintained by Martin KrugerLast reviewed

Overview

Mega menus are large dropdown panels in site navigation that contain multiple groups of links, often organized by category. A common mistake is using role="menu" and role="menuitem" for these panels. The ARIA menu pattern is designed for application-style menus (like a text editor’s File menu), not for website navigation. When screen readers encounter role="menu", they enter a special interaction mode that swallows Tab, expects Arrow key navigation, and breaks the normal browsing experience. The correct pattern for mega menus is the disclosure pattern: a <button> with aria-expanded that toggles a panel of plain links.

WCAG Criteria:

Key requirements:

  • Use <nav aria-label="Main"> as the landmark wrapper
  • Use <button aria-expanded="false/true"> as the disclosure trigger for each category — never a link
  • The dropdown panel is a plain <div> containing <ul> with <a> links — NO role="menu" or role="menuitem"
  • Tab moves through all focusable elements in the open panel naturally
  • Escape closes the open panel and returns focus to its trigger button
  • Never use role="menu" for site navigation — it changes screen reader interaction mode

ARIA Menu Roles vs. Disclosure Pattern for Mega Navigation

Inaccessible
View inaccessible code
<!-- role="menu" on site nav — JAWS/NVDA enter application mode --> <nav> <ul role="menubar"> <li role="none"> <a role="menuitem" href="#" aria-haspopup="true" aria-expanded="false">Products</a> <div role="menu"> <a role="menuitem" href="/analytics">Analytics</a> <a role="menuitem" href="/automation">Automation</a> <a role="menuitem" href="/servers">Servers</a> <a role="menuitem" href="/storage">Storage</a> </div> </li> <li role="none"> <a role="menuitem" href="/pricing">Pricing</a> </li> <li role="none"> <a role="menuitem" href="/about">About</a> </li> </ul> </nav>
Accessible
View accessible code
<!-- Disclosure pattern — no menu roles, Tab works naturally --> <nav aria-label="Main"> <ul> <li> <button aria-expanded="false" onclick=" var expanded = this.getAttribute('aria-expanded') === 'true'; this.setAttribute('aria-expanded', String(!expanded)); var panel = document.getElementById('mega-panel'); if (!expanded) { panel.removeAttribute('hidden'); } else { panel.setAttribute('hidden', ''); } "> Products </button> <div id="mega-panel" hidden onkeydown=" if (event.key === 'Escape') { this.setAttribute('hidden', ''); var btn = this.previousElementSibling; btn.setAttribute('aria-expanded', 'false'); btn.focus(); } "> <p>Software</p> <ul> <li><a href="/analytics">Analytics</a></li> <li><a href="/automation">Automation</a></li> </ul> <p>Hardware</p> <ul> <li><a href="/servers">Servers</a></li> <li><a href="/storage">Storage</a></li> </ul> </div> </li> <li><a href="/pricing">Pricing</a></li> <li><a href="/about">About</a></li> </ul> </nav>

What’s wrong with role="menu" on site navigation?

  • role="menubar" and role="menu" cause JAWS and NVDA to enter a special interaction mode where Tab no longer moves between links — Arrow keys are expected instead
  • Users who do not know the application menu keyboard model (Arrow keys, Home/End, letter keys) will be trapped
  • <a> elements with role="menuitem" lose their link semantics — screen readers stop announcing “link” and say “menu item” instead
  • The trigger is an <a> link acting as a button, which confuses the semantic model — links navigate, buttons perform actions

Predicted semantic summary (exact output varies):

VersionAnnouncement
Inaccessible (role=“menu”)“Products, menu item. Menu. Analytics, menu item. Automation, menu item.” — Tab may not work, Arrow keys expected
Accessible (disclosure)“Main, navigation. Products, collapsed, button” — on activate: “Products, expanded, button. Analytics, link. Automation, link.” — Tab moves naturally through links

Why the disclosure pattern is correct for mega menus:

  • <button aria-expanded> clearly communicates the toggle state
  • The panel contains plain <a> links that behave exactly as users expect — Tab moves through them
  • Escape closes the panel and returns focus to the trigger button, providing a clear exit
  • No special interaction mode is activated, so all normal keyboard behavior is preserved
  • The APG (ARIA Authoring Practices Guide) explicitly recommends the disclosure pattern for site navigation

Coordinate behavior across breakpoints: the same destinations and current-page indication should remain available on touch, keyboard, pointer, and zoomed layouts. Closing on Escape and returning focus is useful when the panel was opened from the trigger; do not trap Tab inside ordinary site navigation.


Resources