Accessibility 10 min read

Accessible navigation: menus, keyboard access and visible focus done properly

Skip links, visible focus, dropdowns that open on Enter, and a five-minute test you can run without a mouse.

On this page 10 sections

Put your mouse in a drawer and try to reach your own contact page. It’s a quick test of accessible navigation, and on many business websites it stalls within a few key presses: the services menu opens only on hover, the focus highlight vanishes somewhere in the header, or the phone menu button is announced as nothing more than “button”.

The fix is a handful of patterns, cheap at the design stage and tiresome to retrofit, that let people get around by keyboard, screen reader, voice control, switch device or heavy zoom. This guide covers how menus behave; website navigation design covers structure and labels.

The short answer

Accessible navigation lets people reach every page by keyboard, screen reader or voice as easily as with a mouse. It needs:

  • A skip to content link first in the tab order, and a focus order that matches the visual order.
  • A visible focus indicator that sticky bars and banners never cover.
  • Dropdowns that open on click, Enter or Space, not hover alone, and close with Escape.
  • A mobile menu button with a clear name and an open or closed state.
  • Landmarks, a marked current page, tappable targets and consistent menus across pages and languages.

Who relies on accessible navigation, and what WCAG asks

Keyboard navigation is how many people with motor impairments, tremors or repetitive strain injuries get around the web. Screen reader users jump between headings, landmarks and links from the keyboard. Voice control users say what they see: “click Services”. People who zoom right in get the mobile layout, menu and all, on a desktop screen. Our web accessibility guide covers the wider picture.

The WCAG 2.2 criteria that matter most for navigation

Criterion Level What it asks of navigation
2.1.1 Keyboard A Everything works from the keyboard
2.1.2 No Keyboard Trap A Focus can always leave a menu or widget
2.4.1 Bypass Blocks A A way to skip the header repeated on every page
2.4.3 Focus Order A A tab order that makes sense
2.4.7 Focus Visible AA Keyboard focus is always visible
2.4.11 Focus Not Obscured (Minimum) AA The focused item is never completely hidden
1.4.13 Content on Hover or Focus AA Hover menus are dismissible, hoverable and stay open
2.5.3 Label in Name A The name in the code includes the visible text
2.5.8 Target Size (Minimum) AA Targets of at least 24 × 24 CSS pixels, or spaced apart
3.2.3 Consistent Navigation AA The same menu order on every page
4.1.2 Name, Role, Value A Controls expose their name, role and state

Focus Not Obscured and Target Size are new in 2.2; WCAG 2.2 explained walks through every addition.

A skip link is the first focusable element on the page: hidden until someone presses Tab, then one Enter away from the main content. Screen reader users can jump by landmark, but browsers give sighted keyboard users no built-in way to do that, so without a skip link they tab through the whole menu on every page.

  • Label it plainly: “Skip to content”.
  • Point it at id="main" on the main element, on every page template.
  • Hide it off-screen until focused, never with display: none, which removes it from the keyboard route.
  • Show it in front of the sticky header, not behind it.

Focus order that follows the page

The Tab key follows the order of the HTML, not the layout, and problems live in the gap:

  • CSS reordering. Flexbox and grid can rearrange the header visually while the tab order stays put.
  • Positive tabindex values override the natural order, almost always for the worse.
  • Closed menus that stay focusable. An off-canvas menu slid out of view can swallow a dozen Tab presses while nothing visible happens. Take it out of the tab order with the hidden attribute, display: none or inert.

A focus indicator people can actually see

For a keyboard user, the focus indicator does the job of the mouse pointer. Without it, every press of Tab is a guess.

Restyle it, don’t remove it

The classic failure is outline: none, added because someone disliked the ring appearing after a mouse click. Use the :focus-visible selector instead: browsers apply it for keyboard focus but usually not when a link or button is clicked, so the ring shows for the people who need it.

A good focus indicator is:

  • Noticeable: 2 to 3 pixels thick, slightly offset from the element.
  • High contrast: at least 3:1 against adjacent colours, WCAG’s Non-text Contrast benchmark for interface components (our guide to colour contrast and accessible typography shows how to check).
  • Two-tone where the site has light and dark sections.
  • Designed, not improvised: the same on every link, button and field, and drawn in the design files next to hover states.

The AAA criterion 2.4.13 Focus Appearance sets exact size and contrast targets worth borrowing.

Focus Not Obscured (Minimum) says the focused element must not be entirely hidden by content the site adds; the AAA version allows no overlap at all. The usual culprits: a sticky header that links scroll beneath as someone tabs back up the page, a cookie banner left open while visitors tab behind it, and chat bubbles or sticky “Call” bars over links near the bottom.

CSS scroll-padding makes the browser leave room for fixed bars when it scrolls focus into view:

/* Leave room for fixed bars when focus scrolls the page */
html {
  scroll-padding-top: 80px;    /* sticky header height */
  scroll-padding-bottom: 96px; /* bottom bar or chat button */
}

/* A two-tone ring that shows on light and dark backgrounds */
:focus-visible {
  outline: 3px solid #111;
  outline-offset: 2px;
  box-shadow: 0 0 0 2px #fff;
}

For cookie banners, the W3C’s guidance on this criterion suggests making the banner modal until it’s answered, leaving room for it with scroll padding, or closing it once focus moves on.

Accessible dropdown menus that don’t depend on hover

Hover-only menus fail keyboard users, who can’t hover; touchscreen users, whose tap may open the parent page instead; and magnifier users, whose pointer drifts off the panel as they pan to read it.

The disclosure pattern

An accessible dropdown menu is a disclosure: a button that shows and hides a list of links.

  1. The parent item is a button with aria-expanded="false", switching to true while open.
  2. Enter, Space, a click or a tap opens it.
  3. Tab moves through its links in order, then on to the next top-level item.
  4. Escape closes it and returns focus to the button.
  5. It closes when focus or a click moves elsewhere.

Hover can open the menu too, provided click works, the panel stays open as the pointer moves into it, and Escape dismisses it.

Decide what the parent item does

“Services” can’t both open a dropdown and go to the services page. For most business sites, make it a button with “All services” as the first link inside, which works the same on every device. Where many visitors want the parent page itself, keep the link and add a separate arrow button beside it, named something like “Services submenu”.

Leave out the ARIA menu roles

Some themes and menu plugins add menu and menubar roles to site navigation. Those roles are for application-style menus, like a desktop program’s, and promise arrow-key behaviour that half-finished builds rarely deliver. The W3C’s ARIA Authoring Practices Guide explains why its disclosure navigation example avoids the menu role: typical site navigation doesn’t need the keyboard interactions the menu pattern specifies. Plain buttons and lists of links inside nav are enough.

Mega menus follow the same pattern: links in reading order under short headings, panels that don’t open just because focus passes over their parent, and lower links still reachable when the page is zoomed.

An accessible mobile menu: a labelled button with the right state

On phones, and for desktop users zoomed in far enough, the whole menu sits behind one button.

  • Use a real <button>. A clickable div or icon needs rebuilding before keyboards and screen readers can use it.
  • Show the word “Menu” beside the icon, so voice control users can say “click Menu”. An icon-only button needs aria-label="Menu".
  • Expose its state with aria-expanded, so screen readers announce collapsed or expanded, and keep the name stable.
  • Manage focus. A push-down menu can simply follow the button in the tab order. A full-screen overlay behaves like a dialog: move focus in, keep it there (inert on the rest of the page helps), close it with Escape or a labelled close button, and return focus to the menu button.
  • Use expandable sections for submenus, not flyouts.

A skeleton to brief a developer with:

<a class="skip-link" href="#main">Skip to content</a>
<header>
  <nav aria-label="Main">
    <button type="button" aria-expanded="false" aria-controls="site-menu">
      <svg aria-hidden="true"><!-- icon --></svg>
      Menu
    </button>
    <!-- a small script toggles aria-expanded and shows or hides the list -->
    <ul id="site-menu">
      <li><a href="/services/" aria-current="page">Services</a></li>
      <li><a href="/work/">Work</a></li>
      <li><a href="/contact/">Contact</a></li>
    </ul>
  </nav>
</header>
<main id="main">…</main>

Landmarks, the current page, target size and names

Landmarks

The header, nav, main and footer elements create landmarks that screen reader users can jump between. Use one main per page, and label each nav (“Main”, “Breadcrumb”, “Footer”) without the word “navigation”, which screen readers announce anyway.

Mark the current page

Add aria-current="page" to the current page’s link in the menu and to the last breadcrumb item, and show it visually with an underline or bar, not colour alone.

Targets big enough to tap

Target Size (Minimum) asks for pointer targets of at least 24 × 24 CSS pixels, or enough space around smaller ones; links inside a sentence are exempt. Aim for 44 × 44, which suits fingers better: it’s WCAG’s AAA level (in CSS pixels) and the default control size in Apple’s Human Interface Guidelines for iPhone (in points). Check close buttons, language links, social icons and stacked footer links, and make whole menu rows clickable.

Names that match visible labels

Voice control relies on Label in Name: the name in the code should contain the visible text. A button that reads “Contact” but carries aria-label="Get in touch with our friendly team" may not respond to “click Contact”. If extra context helps, start the hidden name with the visible words.

Consistent navigation across pages and languages

Consistent Navigation asks that menus repeated across pages keep the same relative order. Consistent Help, new at level A in 2.2, applies the same-order rule to help that repeats across pages, such as contact details or a help link.

On a multilingual website, hold every language version to the same rules:

  • Same structure, same order in every language, so switching doesn’t mean relearning the menu.
  • The language switcher in the same place on every page, reachable on phones without scrolling.
  • Each language named in its own language, with a matching lang attribute so screen readers pronounce it correctly.
  • Real links to the equivalent page, not a dropdown that switches language as soon as a keyboard user arrows through it.

The five-minute ‘unplug the mouse’ test

Open your homepage in Chrome, Edge or Firefox (in Safari, first turn on “Press Tab to highlight each item on a webpage” under Settings → Advanced), push the mouse out of reach and:

  1. Press Tab once. A skip link appears. Press Enter, then Tab: focus lands in the main content.
  2. Tab through the header. Every stop shows a clear focus ring, in reading order, with no invisible stops.
  3. Open each dropdown with Enter or Space. Tab through it, press Escape: it closes and focus returns to its button.
  4. Tab to the footer, then Shift+Tab back up. Nothing focused hides under the header, cookie banner or chat button, and focus never gets stuck.
  5. Zoom in until the mobile menu appears. Open it, move through it, close it with Escape; focus returns to the menu button.
  6. Send yourself an enquiry. If you can’t without the mouse, some of your visitors can’t either.

With two more minutes, try a screen reader (VoiceOver via Cmd+F5 on a Mac, or the free NVDA on Windows): the menu button should announce its name, that it is a button, and whether it is expanded.

What to do next

A focus style has to be designed and a dropdown’s behaviour has to be built, so accessible navigation works only when design and development are planned together, before any code is written.

If the test failed at several steps, the header itself is usually the problem: an off-the-shelf menu never built for keyboards, where patching symptoms rarely lasts. Rebuilding it properly, alongside structure, speed and content, is what a website redesign is for. Not sure whether yours can be fixed in place? Tell us what the test turned up, or browse our other accessibility guides.

Written by the PORVIX team

The people who design, build and maintain websites for growing businesses. We write about the questions that come up on real projects, in plain language, and update articles when the advice changes.

Published

How we work

Is your current site costing you enquiries?

A free, plain-language audit, by email within 2 business days.

Get a free audit
Get a free audit

Keep reading

More plain-language guides.

More on accessibility first, then other guides worth reading next.

All insights

Start here

Let’s build a website that brings in business.

Tell us about your project. You’ll hear back from a real person within one business day, with honest advice either way.

  • Free consultation
  • Fixed written quote
  • Your details stay private