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.
Navigation Pattern
<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;
}2. Keep Skip Links First in DOM Order
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
- Test navigation using only the keyboard; note any fails in
notes/retro-log.md. - Add
aria-currentto the navigation link that best represents the current section. - Update mobile menu toggles to reflect
aria-expandedstate (with minimal JS if necessary) and plan CSS fallback. - Document accessibility requirements in
docs/accessibility.mdunder a new "Navigation" section. - 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-expandedupdates when the mobile menu opens/closes.
Code Quality
- Navigation anchors include
aria-currentwhere 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: noneis avoided during menu toggling.
Project Integration
- Accessibility manifesto references navigation behaviors.
- Retro log captures audit results and any remaining gaps.