Accessible Dropdown and Mega Menus for Online Stores
Short answer: An accessible store menu uses a real button with aria-expanded to open each submenu, works with Tab, Enter and Space, closes with Escape, and shows visible focus. Hover can be an extra, never the only way in. Test it with the Tab key and a screen reader.
Navigation is how shoppers find products. When a dropdown or mega menu opens only on mouse hover, cannot be reached with a keyboard, or is silent to a screen reader, some customers cannot browse your store at all. Menus are also one of the first places an automated or manual audit looks, because they appear on every page.
This guide covers the common menu failures, a simple pattern that fixes most of them, and how to test your own menu in about ten minutes. It is general information, not legal advice. For legal questions, talk to an attorney.
The four problems that show up most
Most inaccessible store menus fail in the same ways:
- Hover-only opening. The submenu appears on mouse hover (CSS
:hover) and nothing else. Keyboard and touch users never see it. - Not reachable by keyboard. The top-level item is a
<div>or a link with no real behavior, so Tab skips it or lands on it with no way to open it. - Focus traps. Focus enters the menu and cannot leave, or jumps somewhere unexpected when the menu opens or closes.
- No announced state. A screen reader user hears "Shoes" but not whether the submenu is open or closed, and the mobile menu icon has no name at all.
These map to several WCAG 2.1 success criteria, mainly 2.1.1 Keyboard, 2.4.7 Focus Visible and 4.1.2 Name, Role, Value. You can read the full text of the standard at w3.org.
Use the disclosure pattern, not the ARIA menu pattern
Many developers reach for role="menu" because the word "menu" sounds right. For website navigation, that is usually the wrong choice. The ARIA "menu" role is meant for application-style menus, like the ones in a desktop program, and it changes how screen readers and keyboards behave in ways site visitors do not expect.
For site navigation, the W3C's examples favor a simpler approach called a disclosure: a button that shows or hides a group of links. The button tells assistive technology whether the content is expanded or collapsed. The links inside stay ordinary links in a list.
The ingredients are:
- A real
<button>for each item that has a submenu. aria-expanded="false"or"true"on that button, updated when it toggles.- The submenu as a normal list of links inside a
<nav>. - Escape closes the open submenu and returns focus to its button.
- A visible focus indicator on every button and link.
A working example
Here is a minimal markup structure:
<nav aria-label="Main">
<ul class="menu">
<li><a href="/new">New arrivals</a></li>
<li>
<button type="button" aria-expanded="false" aria-controls="shoes-menu">
Shoes
</button>
<ul id="shoes-menu" hidden>
<li><a href="/shoes/boots">Boots</a></li>
<li><a href="/shoes/sandals">Sandals</a></li>
<li><a href="/shoes/sneakers">Sneakers</a></li>
</ul>
</li>
<li><a href="/sale">Sale</a></li>
</ul>
</nav>
And the script that drives it:
const buttons = document.querySelectorAll('nav button[aria-expanded]');
function closeMenu(button) {
button.setAttribute('aria-expanded', 'false');
document.getElementById(button.getAttribute('aria-controls')).hidden = true;
}
function openMenu(button) {
// Close any other open submenu first
buttons.forEach((b) => { if (b !== button) closeMenu(b); });
button.setAttribute('aria-expanded', 'true');
document.getElementById(button.getAttribute('aria-controls')).hidden = false;
}
buttons.forEach((button) => {
button.addEventListener('click', () => {
const isOpen = button.getAttribute('aria-expanded') === 'true';
isOpen ? closeMenu(button) : openMenu(button);
});
});
document.addEventListener('keydown', (event) => {
if (event.key !== 'Escape') return;
const open = document.querySelector('nav button[aria-expanded="true"]');
if (open) {
closeMenu(open);
open.focus(); // return focus to the trigger
}
});
Because the trigger is a native <button>, Enter and Space already activate it. You do not need to add custom key handling for those. A native button also exposes the correct role to screen readers without extra ARIA.
You can still open the menu on hover as a convenience for mouse users, but hover must not be the only way. If you add hover behavior, make sure the menu stays open while the pointer moves from the button into the submenu, and that it can be dismissed without moving the pointer.
Mega menus: keep the structure simple
A mega menu is the same pattern with more content. A few practical rules keep it manageable:
- Use headings or labeled groups inside the panel only if they help; keep the reading order logical.
- Keep the Tab order matching the visual order. Avoid positive
tabindexvalues. - Do not put non-link content that needs interaction (search fields, carousels) inside a panel unless you can test that they work by keyboard.
- If the panel is large, consider a link to a full category page instead of a sprawling overlay.
If your menu includes a sliding promotional banner, see accessible carousels and sliders.
Visible focus
When someone tabs through the menu, they need to see where they are. A common cause of failure is a stylesheet that removes the outline with outline: none and offers nothing in its place.
nav button:focus-visible,
nav a:focus-visible {
outline: 3px solid #1a1a1a;
outline-offset: 2px;
}
Pick a color with enough contrast against the background of the menu, including dark header bars. See 2.4.7 Focus Visible and the guide on checking color contrast.
The mobile menu toggle needs a name
The "hamburger" button is often an icon with no text. A screen reader announces it as just "button," or reads out a file name. Give it an accessible name and a state:
<button type="button" class="menu-toggle"
aria-expanded="false" aria-controls="mobile-nav">
<svg aria-hidden="true" focusable="false" width="24" height="24">...</svg>
<span class="visually-hidden">Menu</span>
</button>
Alternatively, use aria-label="Menu" on the button. Either way, update aria-expanded when the panel opens and closes, and make sure Escape closes it.
If the mobile panel covers the page like a dialog, check that focus does not escape behind it and that a visible close button exists. If you use a true modal, move focus into it on open and return focus to the toggle on close. Cookie banners and chat widgets often compete with menus for focus, which is covered in cookie banners, popups and chat widgets.
How to test with the keyboard
You need only your keyboard and a few minutes. For a broader routine, see the 5-minute keyboard accessibility test.
- Load your home page and put your mouse aside.
- Press Tab until you reach the main navigation. Can you reach every top-level item?
- On an item with a submenu, press Enter or Space. Does the submenu open?
- Press Tab to move through the submenu links. Is the order logical?
- Press Escape. Does the submenu close and focus return to the button?
- Tab past the menu entirely. Can you leave without getting stuck?
- Watch the screen throughout. Is the focus indicator always visible?
- Repeat at a narrow browser width to test the mobile toggle.
Write down each failure with the page and step where it happened. A dated record of what you tested and fixed is useful later; see why a dated accessibility audit record matters.
How to test with a screen reader
Keyboard testing shows behavior; a screen reader shows what is announced. Free options include NVDA on Windows and VoiceOver on Mac and iPhone. Our screen reader testing basics guide covers setup.
What to listen for:
| Check | What you should hear |
|---|---|
| Landing on a submenu button | Its name, "button," and "collapsed" |
| After opening it | The state changes to "expanded" |
| Landing on the mobile toggle | A meaningful name such as "Menu," not just "button" |
| Moving through submenu links | Each link's text, ideally in a list |
Exact wording varies between screen readers and browsers, so focus on whether the name, role and state are all present rather than on matching a phrase.
Common mistakes to avoid
- Using
<div onclick>or<a href="#">as the trigger instead of a<button>. - Setting
aria-expandedonce in the markup and never updating it. - Hiding the submenu with
opacity: 0or off-screen positioning so that keyboard focus lands on invisible links. Usehiddenordisplay: nonewhen closed. - Adding
role="menu"androle="menuitem"to ordinary navigation links. - Relying on an accessibility overlay to patch the menu. See do accessibility overlay widgets prevent ADA lawsuits?
If your store runs on a hosted platform, the theme or app often controls the menu code. Check what you can change; see Wix and Squarespace, WordPress, or the Shopify lawsuit guide for platform-specific context. If you need help, read hiring someone to fix website accessibility.
Where this fits in the bigger picture
Fixing the menu is one part of a store-wide effort. Checkout, forms and product images matter just as much; see accessible checkout and the WCAG 2.1 AA checklist. If you received a demand letter, read what to do in the first two weeks and consider speaking with an attorney. When you are ready to see where your own menu stands, you can run a free scan.
Frequently asked questions
Can a menu still open on hover?
Yes, as long as hover is not the only way to open it. Keyboard, touch and screen reader users need a button they can activate. If you keep hover, make sure the submenu stays open while the pointer moves into it and can be dismissed without moving the pointer.
Should I use role="menu" for my store navigation?
Usually not. The ARIA menu role is designed for application-style menus and changes keyboard and screen reader behavior. For site navigation, a button with aria-expanded that shows a list of links is simpler and more predictable.
What should happen when a user presses Escape?
The open submenu should close and keyboard focus should return to the button that opened it. This keeps the user from losing their place. The same behavior should apply to the mobile menu panel.
Does the mobile menu icon need text?
It needs an accessible name, which can come from visually hidden text or an aria-label. An icon-only button with no name is announced as just "button," so a screen reader user cannot tell what it does. It should also report whether it is expanded or collapsed.
If I fix my menu, is my site protected from an ADA claim?
No single fix can promise that. A menu is one part of a site, and how courts treat website accessibility claims varies. Fixing known barriers and keeping dated records of your testing are sensible steps, and an attorney can advise on your specific situation.
Related
- A 5-Minute Keyboard Accessibility Test for Your Online Store
- Screen Reader Testing for Business Owners: VoiceOver and NVDA Basics
- WCAG 2.1.1 Keyboard
- WCAG 4.1.2 Name, Role, Value
Proof of Access provides technical accessibility assessments. We are not a law firm and nothing on this page is legal advice. If you have received a demand letter or a lawsuit, talk to an attorney.