Code To Learn logo

Code To Learn

HTML FundamentalsM1 · Semantic Layout & Landmarks

Accessible Navigation Systems

Refine Portfolio Pulse navigation with keyboard support, ARIA labelling, and focus management.

Accessible Navigation Systems

Learning Objectives

By the end of this lesson, you will:

  • Ensure navigation works flawlessly with keyboard-only interaction.
  • Provide accessible names, states, and instructions for menus.
  • Manage focus when mobile menus open and close.
  • Capture navigation accessibility requirements in documentation.

Project Context

Navigation determines how quickly visitors reach the information they need. For Portfolio Pulse, any friction risks losing a recruiter or client. Accessible navigation is also a legal requirement in many regions, so investing time now saves potential remediation later.


<a class="skip-link" href="#main-content">Skip to main content</a>

<header class="site-header" role="banner">
   <nav aria-label="Primary navigation" class="nav-desktop">
      <ul>
         <li><a href="#highlights" aria-current="section">Work</a></li>
         <li><a href="#services">Services</a></li>
         <li><a href="#testimonials">Proof</a></li>
         <li><a href="#contact">Contact</a></li>
      </ul>
   </nav>

   <details class="nav-mobile" data-menu>
      <summary
         aria-haspopup="listbox"
         aria-expanded="false"
         aria-controls="mobile-nav-list"
      >
         Menu
      </summary>
      <nav id="mobile-nav-list" aria-label="Mobile navigation">
         <ul>
            <li><a href="#highlights" aria-current="section">Work</a></li>
            <li><a href="#services">Services</a></li>
            <li><a href="#testimonials">Proof</a></li>
            <li><a href="#contact">Contact</a></li>
         </ul>
      </nav>
   </details>
</header>

<script type="module">
   const menu = document.querySelector("[data-menu]");
   const summary = menu?.querySelector("summary");

   summary?.addEventListener("click", () => {
      const expanded = summary.getAttribute("aria-expanded") === "true";
      summary.setAttribute("aria-expanded", String(!expanded));
   });
   summary?.addEventListener("keydown", (event) => {
      if (
         event.key === "Escape" &&
         summary.getAttribute("aria-expanded") === "true"
      ) {
         summary.click();
         summary.focus();
      }
   });
</script>

Reinforce expectations in docs/accessibility.md:

### Navigation

-  Skip link must remain first focusable element.
-  aria-current="section" set on nav item that matches viewport section.
-  Mobile menu toggles aria-expanded on open/close; Escape closes menu and
   returns focus.
-  Skip link, menu summary, and CTA retain visible focus styles (3px outline).
-  Avoid display:none on nav lists; use [open] state styling instead.

Log remaining gaps in notes/retro-log.md so Module 1 CSS can style the skip link and mobile menu state effectively.


✅ Best Practices

1. Provide Visible Focus Styles

Why: Focus indicators help keyboard users stay oriented while navigating the menu and skip link.

.site-header a:focus {
   outline: 3px solid var(--accent);
   outline-offset: 4px;
}

Why: Placing skip links first ensures they are the first tab stop and immediately accessible.

<a class="skip-link" href="#main-content">Skip to main content</a>

❌ Common Mistakes

1. Hiding Navigation with display: none

Problem: display: none removes elements from the accessibility tree.

.nav-mobile ul {
   display: none;
}

Solution:

.nav-mobile[open] ul {
   display: block;
}

2. Forgetting to Update aria-expanded

Problem: Screen readers rely on aria-expanded to know menu state.

<!-- Bad: static aria-expanded attribute -->
<summary aria-expanded="false">Menu</summary>

Solution:

summary.setAttribute("aria-expanded", String(!expanded));

🔨 Implement in Portfolio Pulse

Task: Audit and Enhance Navigation Accessibility

  1. Test navigation using only the keyboard; note any fails in notes/retro-log.md.
  2. Add aria-current to the navigation link that best represents the current section.
  3. Update mobile menu toggles to reflect aria-expanded state (with minimal JS if necessary) and plan CSS fallback.
  4. Document accessibility requirements in docs/accessibility.md under a new "Navigation" section.
  5. Commit with git commit -am "feat: harden navigation accessibility".

Expected Result

Navigation works smoothly for keyboard and screen reader users, and documentation captures the requirements for future styling.



✅ Validation Checklist

Functionality

  • Tab order matches the documented focus path.
  • aria-expanded updates when the mobile menu opens/closes.

Code Quality

  • Navigation anchors include aria-current where appropriate.
  • Skip link is the first focusable element in the DOM.

Understanding

  • You can describe how focus behaves when the mobile menu opens.
  • You know why display: none is avoided during menu toggling.

Project Integration

  • Accessibility manifesto references navigation behaviors.
  • Retro log captures audit results and any remaining gaps.