Accessibility pattern · Navigation

Mega menu

The panel is a disclosure: one button with aria-expanded opens it, and inside are ordinary headings, lists and links in reading order. Tab walks through it a group at a time; Escape, a click outside or tabbing past the end closes it.

WCAG criteria
6
Keyboard rules
4
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.

Aangan

You are on

Rugs

This area stands in for the page. Choose a link in the menu: it becomes the current page, the panel 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
Enter or SpaceOn Products, opens or closes the panel. Focus stays on the button.
TabMoves into the open panel and through its links a group at a time, then on to the next item in the navigation, which closes the panel.
ShiftTabMoves back through the panel to the Products button, which keeps it open.
EscapeCloses the panel and returns focus to the Products button.

Screen readers

What it announces

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

WhenExpected announcement
Tab reaches the buttonProducts, button, collapsed
Enter opens the panelExpanded
Tab moves into the first groupLiving room, list, 3 items. Sofas, link
Tab reaches the featured itemJaipur block prints, link
Escape closes the panelProducts, button, collapsed

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-mega-menu" data-ap-mega-menu>
  <header class="ap-mega-menu__bar">
    <a class="ap-mega-menu__brand" href="#mega-home">
      <svg class="ap-mega-menu__mark" viewBox="0 0 32 32" aria-hidden="true" focusable="false"><path d="M4 15 16 5l12 10v12H4Z"/><path d="M13 27v-7h6v7"/></svg>
      <span>Aangan</span>
    </a>
    <nav class="ap-mega-menu__nav" aria-label="Main">
      <ul class="ap-mega-menu__list">
        <li class="ap-mega-menu__item">
          <button type="button" class="ap-mega-menu__top ap-mega-menu__toggle" aria-expanded="false" aria-controls="mega-products">
            Products
            <svg class="ap-mega-menu__chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6"/></svg>
          </button>
          <div class="ap-mega-menu__panel" id="mega-products" hidden>
            <div class="ap-mega-menu__groups">
              <div class="ap-mega-menu__group">
                <h3 class="ap-mega-menu__group-name" id="mega-living">Living room</h3>
                <ul class="ap-mega-menu__links" aria-labelledby="mega-living">
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-sofas">Sofas</a><p class="ap-mega-menu__desc">Seaters and sofa beds</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-coffee-tables">Coffee tables</a><p class="ap-mega-menu__desc">Sheesham, cane and marble</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-rugs" aria-current="page">Rugs</a><p class="ap-mega-menu__desc">Dhurries and wool rugs</p></li>
                </ul>
              </div>
              <div class="ap-mega-menu__group">
                <h3 class="ap-mega-menu__group-name" id="mega-bedroom">Bedroom</h3>
                <ul class="ap-mega-menu__links" aria-labelledby="mega-bedroom">
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-beds">Beds</a><p class="ap-mega-menu__desc">Storage beds and four-posters</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-wardrobes">Wardrobes</a><p class="ap-mega-menu__desc">Two- and three-door</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-bedding">Bedding</a><p class="ap-mega-menu__desc">Sheets, quilts and covers</p></li>
                </ul>
              </div>
              <div class="ap-mega-menu__group">
                <h3 class="ap-mega-menu__group-name" id="mega-kitchen">Kitchen and dining</h3>
                <ul class="ap-mega-menu__links" aria-labelledby="mega-kitchen">
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-dining">Dining tables</a><p class="ap-mega-menu__desc">Four- to eight-seaters</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-cookware">Cookware</a><p class="ap-mega-menu__desc">Cast iron, brass and steel</p></li>
                  <li class="ap-mega-menu__entry"><a class="ap-mega-menu__link" href="#mega-tableware">Tableware</a><p class="ap-mega-menu__desc">Stoneware and copper</p></li>
                </ul>
              </div>
            </div>
            <div class="ap-mega-menu__feature">
              <span class="ap-mega-menu__art" aria-hidden="true"></span>
              <div class="ap-mega-menu__feature-text">
                <p class="ap-mega-menu__kicker">New collection</p>
                <a class="ap-mega-menu__feature-link" href="#mega-jaipur">Jaipur block prints</a>
                <p class="ap-mega-menu__feature-desc">Hand-printed cotton bedding in indigo and madder red.</p>
              </div>
              <svg class="ap-mega-menu__arrow" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
            </div>
          </div>
        </li>
        <li class="ap-mega-menu__item"><a class="ap-mega-menu__top" href="#mega-offers">Offers</a></li>
        <li class="ap-mega-menu__item"><a class="ap-mega-menu__top" href="#mega-stores">Stores</a></li>
        <li class="ap-mega-menu__item"><a class="ap-mega-menu__top" href="#mega-help">Help</a></li>
      </ul>
    </nav>
  </header>

  <div class="ap-mega-menu__page">
    <p class="ap-mega-menu__page-kicker">You are on</p>
    <h3 class="ap-mega-menu__page-name" id="mega-page" tabindex="-1" data-ap-page>Rugs</h3>
    <p class="ap-mega-menu__page-text">This area stands in for the page. Choose a link in the menu: it becomes the current page, the panel 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

    Group names are headings and each list is labelled by its heading, so the panel's structure is exposed, not just drawn in columns.

  • 1.3.2 Meaningful Sequence Level A

    The source runs group by group and then the featured item: the order the columns read in, and the order Tab follows.

  • 1.4.10 Reflow Level AA

    In a narrow space the columns stack into one and the panel joins the page flow, so nothing is cut off or scrolls sideways.

  • 2.1.1 Keyboard Level A

    The panel opens, closes and is moved through with the keyboard alone.

  • 2.4.3 Focus Order Level A

    The panel follows its button in the DOM, so the next Tab goes straight into it, and Escape puts focus back on the button.

  • 4.1.2 Name, Role, Value Level A

    The button exposes aria-expanded and aria-controls, and the panel is hidden with the hidden attribute while it is closed.

Usage

When to use it

Use it

  • Large catalogues or sites where a section holds a dozen or more pages that people browse by category.
  • When a few words of description help people choose between similar items.

Use something else

  • A section with five or fewer links: a simple submenu is quicker to scan.
  • Phones as the main layout: use a navigation drawer and let the groups become sections in it.

Common failures

How it usually goes wrong

  • Opening on hover

    A panel that springs open as the pointer crosses the header covers the page by accident, and closes before keyboard and touch users can reach it.

  • A grid of links with no headings

    Columns look grouped, but without headings a screen reader hears one long list of links. Headings name each group, and each list is labelled by its heading.

  • Columns laid out against the source

    CSS grid can place a column first on screen while it comes last in the source, and then Tab jumps around. Keep the two orders the same.

  • role=menu and arrow-key traps

    Treating the panel as an ARIA menu forces arrow-key movement across a two-dimensional layout nobody can predict. Links and Tab are enough.

  • Descriptions inside the link name

    Putting the description inside the link makes every link a sentence long. Here it follows the link: read in order, but not part of its name.

Notes

Building it

  • The panel sits inside the navigation landmark, straight after its button in the source, so no focus management is needed to reach it.
  • Close the panel when focus leaves it, not when the pointer does: the path from the button to a far column often crosses empty space.
  • In a narrow space the columns stack and the panel becomes part of the page flow instead of floating over it, so nothing is cut off.
  • The featured item's artwork is decoration, drawn in CSS and hidden from assistive technology; its link says where it goes on its own.
  • In the demo the links stand in for pages: choosing one marks it current, closes the panel and moves focus to the page heading.

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