Mega menus pack dozens of links into a panel that drops from the header, and they break for keyboard and screen reader users in predictable ways: the panel only opens on hover, focus escapes behind it, or Escape does nothing. An accessible mega menu fixes all three with a pattern that is simpler than most developers expect.
This guide covers the disclosure pattern, the exact keyboard interactions to wire up, focus management that does not trap or strand users, and why hover alone fails. The examples are framework-agnostic — the same logic applies whether you are in React, Vue, or plain HTML.
Use the disclosure pattern, not the ARIA menu pattern
The single most common mistake is reaching for role="menu" and role="menuitem". That ARIA menu pattern is built for application command menus — think a desktop app's File menu — where arrow keys move a single tab stop between commands. Site navigation is a list of links, and screen reader users expect to Tab through links normally. Forcing the menu role onto navigation hides the link semantics and surprises everyone.
For an accessible mega menu, model each top-level trigger as a disclosure: a real button that shows or hides a panel of links. The button carries two attributes that do the heavy lifting:
- aria-expanded="true" or "false" — announces open/closed state and updates on every toggle.
- aria-controls="panel-id" — points at the id of the panel it reveals.
A minimal trigger looks like this: <button aria-expanded="false" aria-controls="products-panel">Products</button>, followed by <div id="products-panel" hidden>...</div>. Inside the panel, use a plain <ul> of links, ideally grouped under <h3> headings that match the visual columns. Those headings give screen reader users a navigable structure and reinforce a sane document outline.
One nuance: if the trigger label is also a real destination (for example Products links to /products), do not make it a button that swallows the click. Render a link for the destination and a small adjacent toggle button for the panel, so users can navigate or expand independently.
Wire up the keyboard interaction
WCAG 2.1.1 Keyboard requires every function to be operable without a pointer, and 2.1.2 No Keyboard Trap requires that focus can always leave. For a disclosure-based mega menu the keyboard contract is short and predictable:
- Enter or Space on the trigger toggles the panel and flips aria-expanded.
- Tab moves into the panel links in DOM order, then out to the next trigger — no roving tabindex, no arrow-key hijacking needed.
- Escape closes the open panel and returns focus to the trigger that opened it.
- Tabbing past the last link in the panel closes it and continues to the next header item, so focus order matches visual order.
Because you are using real buttons and links, the browser gives you Enter, Space, and Tab for free. The only handlers you must write are the Escape-to-close behavior and the logic that closes an open panel when focus moves to a different trigger. Avoid building arrow-key navigation between columns unless you have a strong reason — it adds complexity and conflicts with screen reader reading keys. For the broader principles behind this, see our keyboard accessibility guide.
Manage focus so it never gets lost
Focus management is where mega menus quietly fail audits. Three rules keep it correct.
Return focus on close
When the user presses Escape or clicks away, move focus back to the trigger button. If you let focus fall to the top of the document or vanish, keyboard users lose their place. This is the same return-focus discipline used in accessible modals and dialogs — though a mega menu is not a modal and must not trap focus.
Keep the focused item visible
WCAG 2.4.7 Focus Visible requires a clear focus indicator, and WCAG 2.2 adds 2.4.11 Focus Not Obscured (Minimum), at Level AA. A sticky header that overlaps the panel can hide the very link a keyboard user just focused. Test by tabbing slowly and confirming each focused link sits fully in view, with a visible outline that meets 3:1 contrast against its background.
Close other panels
Only one panel should be open at a time. When a new trigger is activated or receives focus, collapse the previously open one and reset its aria-expanded to false, so assistive tech state never drifts out of sync with what is on screen.
Do not rely on hover alone
Hover-only mega menus exclude every keyboard user, most screen reader users, and anyone on a touch device, where there is no hover state at all. Hover can be a nice enhancement, but it must never be the sole trigger.
If you do open panels on hover, WCAG 1.4.13 Content on Hover or Pointer (Level AA) sets three requirements: the content must be dismissable (Escape closes it without moving the pointer), hoverable (the user can move the pointer onto the panel without it vanishing), and persistent (it stays until dismissed or the pointer leaves). In practice that means:
- Add a short open-intent delay (roughly 100–300 ms) so panels do not flash as the pointer crosses them.
- Add a close delay so a small gap between trigger and panel does not snap it shut.
- Always pair hover with click and focus activation, and always honor Escape.
Also respect target sizing: WCAG 2.2's 2.5.8 Target Size (Minimum), at Level AA, asks for interactive targets of at least 24 by 24 CSS pixels, which matters when you cram many links into tight columns. And confirm link text meets the 4.5:1 contrast minimum for normal text — you can verify pairs with our contrast checker.
Why this matters for compliance
Navigation is on every page, so a broken mega menu is a site-wide barrier. Under the European Accessibility Act (Directive (EU) 2019/882), which applies from 28 June 2025, covered services such as e-commerce and banking must meet WCAG 2.2 Level A and AA via the harmonised standard EN 301 549 — and the criteria above (2.1.1, 2.1.2, 2.4.7, 2.4.11, 1.4.13, 4.1.2) are all in scope. The same baseline underpins national laws like Germany's Barrierefreiheitsstärkungsgesetz (BFSG). For the regulatory picture, see our overview of the European Accessibility Act.
Automated tools catch missing aria-expanded and contrast failures, but keyboard order and focus return need a human pass — tab through every menu with the mouse untouched. Run a free scan with AccessScan to flag the machine-detectable issues first, then work through the keyboard and focus checks by hand.