Accessible Navigation Menus: Best Practices for WCAG Compliance
Navigation menus are critical wayfinding tools that must work for all users, including those using keyboards and screen readers. This guide covers creating accessible navigation that meets WCAG 2.0/2.1 Level AA requirements.
WCAG Requirements
2.4.1 Bypass Blocks: Skip navigation link provided
2.4.3 Focus Order: Logical tab sequence
2.4.5 Multiple Ways: Multiple navigation methods
2.4.8 Location: Current page indicated
4.1.2 Name, Role, Value: Proper semantic markup
Basic Accessible Navigation
Semantic HTML
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
Key elements:
<nav>landmarkaria-labeldescribes navigation purpose<ul>/<li>for list structure<a>for links
Skip Navigation Link
Allow keyboard users to bypass repetitive navigation:
<a href="#main-content" class="skip-link">Skip to main content</a>
<nav>...</nav>
<main id="main-content">
<!-- Page content -->
</main>
CSS:
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
position: static;
left: auto;
}
Result: Hidden until focused, then visible.
Dropdown Menus
Hover + Click Pattern
<nav aria-label="Main">
<ul>
<li class="has-dropdown">
<button aria-expanded="false" aria-controls="services-menu">
Services
</button>
<ul id="services-menu" hidden>
<li><a href="/web-design">Web Design</a></li>
<li><a href="/development">Development</a></li>
<li><a href="/consulting">Consulting</a></li>
</ul>
</li>
</ul>
</nav>
JavaScript (toggle dropdown):
button.addEventListener('click', function() {
const expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !expanded);
const menu = document.getElementById(this.getAttribute('aria-controls'));
menu.hidden = expanded;
});
Keyboard support:
- Enter/Space: Toggle dropdown
- Escape: Close dropdown
- Arrow keys: Navigate menu items
ARIA Attributes
aria-expanded: Indicates dropdown state (true/false)
aria-controls: Points to dropdown menu ID
hidden: Hides dropdown when closed
Mega Menus
For complex multi-level navigation:
<nav aria-label="Main">
<ul>
<li>
<button aria-expanded="false" aria-controls="products-mega">
Products
</button>
<div id="products-mega" class="mega-menu" hidden>
<div class="mega-column">
<h3>Category 1</h3>
<ul>
<li><a href="/product-1">Product 1</a></li>
<li><a href="/product-2">Product 2</a></li>
</ul>
</div>
<div class="mega-column">
<h3>Category 2</h3>
<ul>
<li><a href="/product-3">Product 3</a></li>
<li><a href="/product-4">Product 4</a></li>
</ul>
</div>
</div>
</li>
</ul>
</nav>
Best practices:
- Use headings for sections
- Keep structure flat (avoid deep nesting)
- Ensure keyboard navigation works
- Close on Escape key
- Consider focus trap for large menus
Mobile Navigation (Hamburger Menu)
<button
class="mobile-menu-toggle"
aria-expanded="false"
aria-controls="mobile-menu"
aria-label="Menu"
>
☰
</button>
<nav id="mobile-menu" hidden>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
JavaScript:
toggle.addEventListener('click', function() {
const expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !expanded);
menu.hidden = expanded;
// Update label
this.setAttribute('aria-label', expanded ? 'Menu' : 'Close menu');
});
Considerations:
- Focus management when opening/closing
- Trap focus in mobile menu when open
- Close on Escape
- Prevent background scroll when open
Current Page Indication
Visual + Programmatic
<nav aria-label="Main">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about" aria-current="page">About</a></li>
<li><a href="/services">Services</a></li>
</ul>
</nav>
CSS:
[aria-current="page"] {
font-weight: bold;
text-decoration: underline;
}
aria-current values:
page: Current page in navigationstep: Current step in multi-step processlocation: Current location in flowdate: Current date in calendartime: Current timetrue: Generic current item
Breadcrumb Navigation
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/products/laptops">Laptops</a></li>
<li aria-current="page">MacBook Pro</li>
</ol>
</nav>
Benefits:
- Shows location in site hierarchy
- Provides alternative navigation path
- Uses
<ol>(ordered list) semantically
Multiple Navigation Landmarks
When multiple navs exist, label them:
<nav aria-label="Main">
<!-- Primary navigation -->
</nav>
<nav aria-label="Secondary">
<!-- Secondary navigation -->
</nav>
<nav aria-label="Footer">
<!-- Footer navigation -->
</nav>
Screen reader announcement: "Main navigation", "Secondary navigation", etc.
Focus Styles
Ensure visible focus indicators:
nav a:focus,
nav button:focus {
outline: 3px solid #0066cc;
outline-offset: 2px;
}
Never: outline: none without replacement.
Testing Checklist
- [ ] Use semantic HTML (
<nav>,<ul>,<li>,<a>) - [ ] Provide aria-label on
<nav> - [ ] Skip navigation link available
- [ ] All links keyboard accessible
- [ ] Dropdowns work with keyboard (Enter, Escape, Arrows)
- [ ] aria-expanded indicates dropdown state
- [ ] Current page indicated (aria-current)
- [ ] Focus indicators visible
- [ ] Mobile menu keyboard accessible
- [ ] Screen reader announces navigation properly
- [ ] Tab order is logical
- [ ] Multiple navs properly labeled
Common Mistakes
Hover-only dropdowns: No keyboard access
Missing aria-expanded: Screen readers don't know dropdown state
No skip link: Keyboard users must tab through everything
Unlabeled navs: Multiple navs indistinguishable
Poor focus indicators: Can't see what's focused
Conclusion
Accessible navigation requires semantic HTML, keyboard support, proper ARIA, and screen reader compatibility. Use <nav> landmarks, provide skip links, ensure dropdowns work with keyboard, and indicate current page with aria-current.
For monitoring navigation accessibility across your site, BrowseCheck provides continuous WCAG compliance scanning. Accessible navigation benefits all users—clear, keyboard-accessible menus improve usability for everyone.