Accessible Navigation Menus: Best Practices for WCAG Compliance

accessible componentsUI accessibilityweb componentsARIA patternsBrowseCheck
·4 min read

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> landmark
  • aria-label describes 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 navigation
  • step: Current step in multi-step process
  • location: Current location in flow
  • date: Current date in calendar
  • time: Current time
  • true: 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.