Mobile Navigation Menu

Create accessible mobile navigation with hamburger menus. Ensure touch targets are large enough and expandable sections are properly announced.

intermediate
2.4.5 Multiple Ways (Level AA)2.5.5 Target Size (Level AAA)3.2.1 On Focus (Level A)

The Bad (Inaccessible)

<div class="mobile-menu" onclick="toggleMenu()">
  <div class="hamburger"></div>
</div>
<div class="menu-items" style="display:none">
  <a href="/">Home</a>
  <a href="/about">About</a>
</div>

Accessibility-Ready Code

<!-- Gold Standard: Button with ARIA states -->
<button
  type="button"
  aria-expanded="false"
  aria-controls="mobile-menu"
  aria-label="Open navigation menu"
  class="mobile-toggle"
>
  <span class="hamburger" aria-hidden="true">
    <span></span>
    <span></span>
    <span></span>
  </span>
</button>

<!-- Collapsible menu panel -->
<div id="mobile-menu" class="menu-panel" hidden>
  <nav aria-label="Mobile 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>
</div>

<script>
  const toggle = document.querySelector('.mobile-toggle');
  const menu = document.getElementById('mobile-menu');

  toggle.addEventListener('click', () => {
    const isExpanded = toggle.getAttribute('aria-expanded') === 'true';
    toggle.setAttribute('aria-expanded', !isExpanded);
    toggle.setAttribute('aria-label', isExpanded ? 'Open navigation menu' : 'Close navigation menu');
    menu.hidden = isExpanded;
  });
</script>

The Standard

Mobile navigation must be fully accessible to keyboard and screen reader users. The key is using proper button elements with ARIA states that update dynamically.

WCAG Criteria

  • 2.4.5 Multiple Ways: Provide multiple ways to access content.
  • 2.5.5 Target Size: Touch targets at least 44×44px.
  • 3.2.1 On Focus: No context changes on focus.

❌ The Bad (Inaccessible)

What’s Wrong?

  1. Using div with onclick: Not keyboard accessible, no semantic meaning.
  2. No aria-expanded: Screen readers don’t know if menu is open.
  3. Small touch targets: Hard for users with motor impairments.
  4. Missing aria-label: Unclear purpose of the toggle button.

✅ The Good (Accessibility-Ready Code)

Why This Works

  1. Proper Button Element: Inherently keyboard accessible and semantic.
  2. Dynamic ARIA States: aria-expanded updates to reflect current state.
  3. Descriptive Labels: Button label changes from “Open” to “Close” based on state.
  4. Minimum Touch Size: 44×44px meets WCAG AAA requirements.

Accessibility Checklist

  • Use <button> instead of <div> for the toggle.
  • Add aria-expanded that updates dynamically (true/false).
  • Include aria-controls pointing to the menu panel ID.
  • Update aria-label to reflect current state (“Open”/“Close”).
  • Ensure toggle is at least 44×44px for touch targets.
  • Add keyboard support (ESC to close, arrow keys for navigation).
  • Use hidden attribute instead of display: none for better AT support.
📍 Perfect Implementation Reference
<button
  type="button"
  aria-expanded="false"
  aria-controls="mobile-menu"
  aria-label="Open navigation menu"
>
  <span aria-hidden="true">☰</span>
</button>

<div id="mobile-menu" hidden>
  <nav aria-label="Mobile navigation">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
    </ul>
  </nav>
</div>

<style>
  button {
    min-width: 44px;
    min-height: 44px;
  }
</style>

<script>
  const button = document.querySelector('button');
  const menu = document.getElementById('mobile-menu');

  button.addEventListener('click', () => {
    const isOpen = button.getAttribute('aria-expanded') === 'true';
    button.setAttribute('aria-expanded', !isOpen);
    button.setAttribute('aria-label', isOpen ? 'Open' : 'Close');
    menu.hidden = isOpen;
  });
</script>

Technical Deep Dive

Screen Reader Announcements

  • NVDA: “Open navigation menu, button, collapsed, double tap to activate”
  • VoiceOver: “Open navigation menu, button, collapsed”
  • TalkBack: “Open navigation menu, button, collapsed, double tap to activate”

Best Practice: Touch Target Size

Mobile navigation buttons must meet WCAG 2.5.5 (Level AAA) touch target requirements: at least 44×44 CSS pixels. This ensures users with motor impairments can easily activate controls. The hamburger button should be clearly labeled with aria-label that updates dynamically to reflect the current state (e.g., “Open menu” vs “Close menu”).

🧪

Interactive Behavioral Lab

💻

Interactive Sandbox

LIVE PREVIEW

🔬 Technical Internals

Understand how this component is processed by the browser and Assistive Technology (AT). This section bridges the gap between visual code and the hidden logic that powers accessibility.

🌲 Accessibility Tree

The data structure used by screen readers to "see" your page. It translates HTML roles and attributes into standardized objects.

⚙️ Event Logic

Expected behavioral standards for keyboard navigation and state transitions. Crucial for users who don't use a mouse.

  • Focus: Highlights via :focus-visible
  • Activation: Responds to Enter and Space
  • Role: Identified as Disclosure

🌐 Browser & Screen Reader Compatibility

Browser
Screen Reader
Status
🍎 Safari
VoiceOver
✓ Safe
🪟 Chrome
NVDA
✓ Safe
🪟 Edge
JAWS
✓ Safe
🦊 Firefox
NVDA
✓ Safe
📱 iOS Safari
VoiceOver
✓ Safe
🤖 Chrome Android
TalkBack
✓ Safe