Navigating sites
A site menu answers three questions: where can I go, where am I now, and how do I open the rest.
Wrapping links in <nav> is only the start. People also need to know the current page, how long the list is, and a menu that still works on a phone.
In one minute
- Put site links in a
<nav>that holds a<ul>. Screen readers then announce "navigation" and "list, 3 items" (that is the count). - If a page has more than one
<nav>, give each its ownaria-label. - Mark the current page with
aria-current="page"and a visible cue that is not colour alone. - A menu button that opens a panel is a disclosure: a real
<button>witharia-expandedandaria-controls. Do not userole="menu"for site navigation.
Think of a shopping mall directory. It lists the shops (the links), has a "You are here" dot (the current page), and shows how many floors there are (the count). A landmark is a labelled area of the page that a screen reader can jump to directly, like a sign saying "Food court this way". A blind user can ask "list the landmarks" and skip straight to the navigation.
1. The nav landmark, and why labels matter
<nav> becomes a navigation landmark. A screen reader can list all landmarks on a page and jump between them.
This very page has two: the curriculum menu at the top and the lesson pager at the bottom. If both just say "navigation", how do you tell them apart?
Add aria-label. Do not put the word "navigation" in the label; the role is already announced.
Lab: the landmarks list
Step 1. Toggle labels and see what a screen reader's landmark list would show for a shop page with two navs.
Only label what repeats. A page with exactly one nav does not need a label, but it never hurts.
Do not wrap every group of links in a <nav>: use it for major blocks such as the main menu, breadcrumbs, and the pager.
2. Show the current page, and the count
Sighted users see a highlighted link. Screen reader users need the same fact in the markup. aria-current="page" adds "current page" to what is announced.
A <ul> adds "list, 3 items", so users know how far the menu goes before they start. Both are free once you use the right elements.
Lab: current page and list semantics
Step 2. Pick the markup. Then Tab to the menu links.
WCAG 1.4.1 forbids colour as the only cue. Use a different weight, underline, or marker too.
Use aria-current="page" for the current page, and aria-current="step" for a step in a process. Do not set it on more than one link in the same menu.
Do not change the link text itself (for example adding "(current)") unless you also keep it short.
3. A menu that works on a narrow screen
On a phone, menus collapse behind a button. That button is a disclosure button: a button that shows or hides a section of the page.
It needs two attributes. aria-expanded says whether the section is open ("collapsed" or "expanded" is announced).
aria-controls points at the section by id. Then the closed panel must really be gone: use the hidden attribute (or display: none).
Hiding with opacity: 0 or by sliding it off screen leaves the links in the tab order, so keyboard users tab through invisible links.
Lab: Menu button
Step 3. Pick how the closed menu is hidden and whether the button states its state. Keep the menu closed and press Tab past the button.
Do not move focus when the menu opens unless the user asked to enter it. The button stays focused, and the next Tab lands on the first link.
If you animate the panel, respect prefers-reduced-motion, and never leave a closed panel in the accessibility tree while it is closed.
Also check the menu at 320 CSS pixels wide and at 400% zoom (WCAG 1.4.10): it must collapse, not scroll sideways.
4. Submenus: hover is not enough
A submenu that opens only on hover is closed to keyboard users, touch users, and anyone using voice control or a switch.
Give the parent a real button with aria-expanded. Click, Enter, and Space then all work.
If you also open on hover, WCAG 1.4.13 asks that the extra content can be dismissed (Escape), hovered over without disappearing, and stays until the user moves away.
Lab: Lessons submenu
Step 4. Pick a version. Try to open the submenu with the mouse, then with the keyboard only.
Do not use role="menu" or role="menuitem" for site navigation. Those roles are for application menus (like File, Edit) and change how screen readers and keyboards work.
A list of links in a disclosure is simpler and more reliable.
5. Quick check: pick the best markup
Which of these builds the best site menu?
Lab: choose a pattern
Common mistakes
| You see | Why it is a problem | Fix |
|---|---|---|
Two <nav> with no labels | Both announced as "navigation". | Unique aria-labels. |
| Current page shown in colour only | Invisible in grayscale and to screen readers. | aria-current="page" + underline or bold. |
Menu closed with opacity: 0 | Invisible links still get focus. | hidden or display: none. |
Menu button without aria-expanded | State is silent. | Update it on every toggle. |
| Hover-only submenu | No keyboard or touch access. | Disclosure button. |
role="menu" on a site menu | Wrong keyboard model for links. | Plain list of links. |
| Menu order changes between pages | Breaks 3.2.3 Consistent Navigation. | Same order on every page. |
- In lab 3, switch to the
hiddenattribute, keep the menu closed, and press Tab. The count of hidden tab stops should be 0. - Open Menu with Enter. The button's expanded state should flip, and the links should become tabbable.
- On a site you use, press Tab once. Is there a skip link? Can you open every submenu without a mouse?
Remember
<nav>+<ul>+<a href>; label each nav when there are several.aria-current="page"plus a non-colour cue.- Menu button:
aria-expandedandaria-controls; closed panel truly hidden. - Submenus open by button, never by hover alone. Keep menu order the same on every page.