Accessibility pattern · Navigation

Site navigation with submenus

Each section is a button that shows or hides a plain list of links, so the navigation stays what it is: links in lists in a nav landmark, not an application menu. Escape, a click outside or moving focus away closes the open list, and aria-current tells everyone which page they are on.

WCAG criteria
6
Keyboard rules
6
Checked with
axe, keyboard and the inspector

Live demo

Try it

Use it with a mouse, a keyboard or a screen reader. The inspector beside it shows what the browser tells assistive technology as you go: focus, state changes and announcements.

Kala Kendra

You are on

This area stands in for the page. Choose a link in the navigation: it becomes the current page, the submenu closes and focus moves to this heading.

The demo works without JavaScript only as far as its HTML does; the inspector needs JavaScript.

Keyboard

Keys it answers to

Every action works without a pointer.

KeyWhat it does
Tab or ShiftTabMoves through the top-level buttons and links, and through an open submenu's links. Moving out of an open submenu closes it.
Enter or SpaceOn a section button, opens or closes its submenu. Focus stays on the button.
EscapeCloses the open submenu and returns focus to its button.
Arrow Right or Arrow LeftMoves to the next or previous top-level item.
Arrow Down or Arrow UpOn a section button, Arrow Down opens its submenu and moves to the first link. Inside a submenu, moves to the next or previous link; Arrow Up from the first link goes back to the button.
Home or EndMoves to the first or last item at the same level.

Screen readers

What it announces

Written from the roles, names and states in the markup.

WhenExpected announcement
Tab reaches a section buttonWhat's on, button, collapsed
Enter opens itExpanded
Tab moves into the submenuList, 4 items. Exhibitions, current page, link
Escape closes itWhat's on, button, collapsed
A link is chosenWorkshops, heading level 3

These are expected announcements, not recordings. Wording and order differ between screen readers and browsers.

Code

Copy the code

The exact files this demo runs on. The styles are served with a prefix that keeps this site's own styles out of the demo; what you copy is the original.

The markup

<div class="ap-site-nav" data-ap-site-nav>
  <header class="ap-site-nav__bar">
    <a class="ap-site-nav__brand" href="#sitenav-home">
      <svg class="ap-site-nav__mark" viewBox="0 0 32 32" aria-hidden="true" focusable="false"><path d="M4 28V14a12 12 0 0 1 24 0v14Z"/><path d="M12 28v-9a4 4 0 0 1 8 0v9"/></svg>
      <span>Kala Kendra</span>
    </a>
    <nav class="ap-site-nav__nav" aria-label="Main">
      <ul class="ap-site-nav__list">
        <li class="ap-site-nav__item">
          <button type="button" class="ap-site-nav__top ap-site-nav__toggle" aria-expanded="false" aria-controls="sitenav-visit">
            Visit
            <svg class="ap-site-nav__chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
          </button>
          <ul class="ap-site-nav__menu" id="sitenav-visit" hidden>
            <li><a class="ap-site-nav__link" href="#sitenav-plan">Plan your visit</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-hours">Opening hours</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-getting-here">Getting here</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-access">Access and facilities</a></li>
          </ul>
        </li>
        <li class="ap-site-nav__item">
          <button type="button" class="ap-site-nav__top ap-site-nav__toggle" aria-expanded="false" aria-controls="sitenav-whats-on">
            What's on
            <svg class="ap-site-nav__chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
          </button>
          <ul class="ap-site-nav__menu" id="sitenav-whats-on" hidden>
            <li><a class="ap-site-nav__link" href="#sitenav-exhibitions" aria-current="page">Exhibitions</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-performances">Performances</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-workshops">Workshops</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-family">Family days</a></li>
          </ul>
        </li>
        <li class="ap-site-nav__item">
          <button type="button" class="ap-site-nav__top ap-site-nav__toggle" aria-expanded="false" aria-controls="sitenav-learn">
            Learn
            <svg class="ap-site-nav__chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
          </button>
          <ul class="ap-site-nav__menu" id="sitenav-learn" hidden>
            <li><a class="ap-site-nav__link" href="#sitenav-courses">Courses</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-schools">For schools</a></li>
            <li><a class="ap-site-nav__link" href="#sitenav-talks">Talks archive</a></li>
          </ul>
        </li>
        <li class="ap-site-nav__item"><a class="ap-site-nav__top" href="#sitenav-shop">Shop</a></li>
        <li class="ap-site-nav__item"><a class="ap-site-nav__top" href="#sitenav-membership">Membership</a></li>
      </ul>
    </nav>
  </header>

  <div class="ap-site-nav__page">
    <p class="ap-site-nav__kicker">You are on</p>
    <h3 class="ap-site-nav__page-name" id="sitenav-page" tabindex="-1" data-ap-page>Exhibitions</h3>
    <p class="ap-site-nav__page-text">This area stands in for the page. Choose a link in the navigation: it becomes the current page, the submenu closes and focus moves to this heading.</p>
  </div>
</div>

WCAG 2.2

What it meets

The success criteria this pattern takes care of, and how.

  • 1.3.1 Info and Relationships Level A

    The navigation is a nav landmark of lists and links, and each submenu is a list that follows its button in the source.

  • 2.1.1 Keyboard Level A

    Every submenu opens, closes and is moved through with the keyboard; the arrow keys are a shortcut on top of Tab, not a requirement.

  • 2.4.3 Focus Order Level A

    Submenus sit straight after their buttons, so Tab order matches what is on screen, and Escape puts focus back where it started.

  • 2.4.7 Focus Visible Level AA

    Every button and link shows a two-pixel focus ring that clears 3:1 against the header and the submenu.

  • 2.4.8 Location Level AAA

    aria-current marks the page you are on, and the section that holds it carries a visible bar.

  • 4.1.2 Name, Role, Value Level A

    Section buttons expose aria-expanded and aria-controls, and the current page's link carries aria-current="page".

Usage

When to use it

Use it

  • A site with a few sections that each hold several pages, shown across a header on wide screens.
  • Navigation that has to work the same way with a mouse, a keyboard and touch.

Use something else

  • More than a handful of links per section, or links that need a description: use a mega menu.
  • Small screens: put the whole navigation in a drawer behind a menu button.
  • Commands in an application, like Edit or View: those belong in a menu button with role=menu.

Common failures

How it usually goes wrong

  • role=menu on site navigation

    Menu roles switch screen readers into application mode and promise arrow-key behaviour most site menus do not have. Links in lists in a nav landmark are what people expect.

  • Submenus that open only on hover

    Hover leaves out keyboard and touch users, and a menu that closes when the pointer drifts off by a pixel fails many mouse users too.

  • A section label that is a link and a toggle at once

    If clicking What's on both loads a page and opens a list, keyboard users only ever get one of them. Use a button, and put an overview link inside the list if the section has a page.

  • No way to close it from the keyboard

    A submenu that ignores Escape and stays open when focus moves on covers the page for keyboard users.

  • The current page shown only by color

    A tinted link is invisible to many people and silent to screen readers. aria-current names it, and a bar marks it.

Notes

Building it

  • Put aria-expanded on the button, not on the list, and hide the list with the hidden attribute so its links leave the Tab order while it is closed.
  • The arrow keys follow the WAI-ARIA disclosure navigation example. They are an extra: Tab alone reaches everything, and it is what most people use.
  • In the demo the links stand in for pages: choosing one marks it with aria-current, closes the submenu and moves focus to the page heading, as a single-page app should. On a site that loads a new page, the server sets aria-current instead, and links to other pages load as normal.
  • The bar under a section button whose list holds the current page is visual only; the link itself says current page.

Sources: WAI-ARIA Authoring Practices: Disclosure navigation menu example · WAI tutorial: Fly-out menus

Checked with axe in light and dark themes, at desktop and phone widths, and by keyboard. Not yet tested with every screen reader and browser pair. Report a correction