Learning Track
Filter content by your role to see the most relevant sections
Navigation Patterns
Design wayfinding users can trust: tabs with the tablist/tab/tabpanel triad, breadcrumbs that expose hierarchy, an unambiguous current-location rule, skip links for keyboard travelers, and navigation that lives in the DOM, not the screenshot.
Why This Matters
Navigation is the interface's promise about geography: you are here, that is there, and this is how the places connect. Users metabolize that promise in seconds and then stop thinking about it—until a pattern breaks it. The tab that navigates like a link but looks like a control. The breadcrumb that names a hierarchy the URL disagrees with. The highlighted item that means "where you are" on one screen and "where your pointer is" on the next.
The system treats navigation as a small set of contracts—each declared in the component contracts (Tabs, Breadcrumbs, Links) and each testable in the accessibility tree, because navigation that only exists visually is geography for sighted pointer users and fog for everyone else.
Core Concepts: Tabs, Breadcrumbs, Location, Escape
Tabs: The ARIA Triad
Tabs are the most-implemented, most-mangled navigation pattern. The contract is a triad— tablist, tab, tabpanel—with wiring between them:
<!-- The triad, wired -->
<div role="tablist" aria-label="View">
<button role="tab" aria-selected="true"
aria-controls="overview-panel" id="overview-tab">
Overview
</button>
<button role="tab" aria-selected="false"
aria-controls="history-panel" id="history-tab">
History
</button>
</div>
<div role="tabpanel" id="overview-panel"
aria-labelledby="overview-tab" tabindex="0">…</div>
// Wiring: tab→panel (aria-controls), panel→tab (aria-labelledby),
// state: aria-selected. Keyboard: arrows move BETWEEN tabs,
// Tab moves INTO the panel. The Tabs contract declares all of it.The behavioral rule that separates implementations: tab selection moves with arrow keys, not Tab—Tab is for leaving the widget. And activation follows focus (selection on arrow) or precedes it (selection on Enter) by declared convention; the system follows the ARIA Authoring Practices' automatic activation for small lists, manual for long ones where scrolling past entries would be punitive.
Breadcrumbs: Hierarchy Made Visible
A breadcrumb is a contract about structure: the trail names the ancestors of the current page, in order, with the current page last and unlinked. The tree contract: nav aria-label="Breadcrumb" (parallel navs must disambiguate for screen reader rosters), an ordered list, separators as decoration (aria-hidden—the screen reader hears the list, not the slashes). The deep rule: the trail must match the URL's truth. A breadcrumb invented for wayfinding charm that disagrees with where pages actually live is a lie users can click.
The Current-Location Rule
Exactly one navigation state means "you are here": aria-current="page" for page-level nav, aria-current="true" for within-page position. It is announced by screen readers, styled by the system's state tokens, and— critically—not confused with hover or focus, which answer different questions ("where is your pointer", "where is your keyboard"). Three states, three meanings, one rule each; the visual system encodes them at different intensities precisely so the channels never collide.
Skip Links and the Keyboard Traveler
Every page with repeated chrome owes the keyboard a way past it: the skip link, first in the DOM, visible on focus, jumping to main content with a real target. This repository's education template ships one— Skip to main content anchored to #main-content—because a keyboard user tabbing through thirty nav links on every page is a wayfinding tax sighted pointer users never pay.
System Roles: Owning the Geography
Design Impact
Designers own the map truth: information architecture that matches the trail, one current-location convention, and nav density decisions (how many destinations before grouping).
Engineering Impact
Engineers own the wiring: the triad's attributes, arrow-key behavior, aria-current maintained by the router not by hand, and parallel navs labeled for the roster.
Accessibility Impact
The a11y function owns the roster test: navigating by landmarks and nav lists must produce a usable map with no sighted assistance—name, order, current.
Design & Code Interplay
The design side of navigation is the annotated map: every destination, the current-state styling at each level, and the trail's agreement with the IA. The two-hour audit that pays for itself: walk the product's nav states and mark each highlighted item with which question it answers—pointer, keyboard, or location. Collisions found here are fixed in tokens, not in screenshots.
The contracts, as the router maintains them:
// Current location is derived, never hand-set
const isActive = (href) => pathname.startsWith(href);
<Links NavLink href="/blueprints" aria-current={isActive('/blueprints')
? 'page' : undefined}>Blueprints</Links>
// The skip link, first in DOM, visible on focus
<a href="#main-content" className={styles.skipLink}>
Skip to main content
</a>
// Parallel navs disambiguate for the roster
<nav aria-label="Breadcrumb">…</nav>
<nav aria-label="Prerequisites">…</nav>Applied Example: A Three-Level Wayfinding System
Assemble the full stack for a documentation area:
- Global nav: five destinations,
aria-currenton the active area, the state token styling it—density kept low enough that scanning beats remembering. - Breadcrumb: area → section → page, matching the URLs exactly, current page unlinked, separators decorative.
- Section tabs: the ARIA triad wired per the contract, arrow-key selection, panels labelled back to their tabs.
- Skip link first in the DOM past the chrome; keyboard path to content in one Enter.
- Verify: the roster pass—landmarks plus nav labels produce the whole map by listening; the e2e walk asserts
aria-currentmatches the route on every page.
Constraints & Trade-offs
- Nav breadth vs depth: flat navs scan faster and cap out (~7 destinations); grouped navs scale and cost a level of thought. The split follows the IA, not the header height.
- Automatic vs manual tab activation: automatic is faster for short lists and hostile for long ones; the contract declares which and why.
- Persistent vs contextual nav: persistent nav preserves orientation and consumes space; contextual (command palettes, menus) inverts both. Mature products mix deliberately.
Verification Checklist
Additional Resources
- ARIA APG tabs pattern — the reference behavior (
https://www.w3.org/WAI/ARIA/apg/patterns/tabs/) - Grid systems — the layouts navs organize (
/blueprints/foundations/grid) - Dialogs & overlays — the other way surfaces move (
/blueprints/ux-patterns/dialogs)
Related Concepts
Reflection Questions
Apply This Concept
Run the roster pass on a product you use: landmarks and nav labels only, no visuals. Could you navigate? Write the missing labels and the collision you found.
Reflect
Your analytics show users open the command palette even for two-hop navigation the nav supports. What is the nav failing to promise, and which contract would you change first?
