This page provides an example of a mega menu with hover support that has been optimized for accessibility. The code is available for resuse as a public GitHub archive with css/mega-menu-styles.css and js/mega-menu.js being the core files.
The Accessible Mega Menu was designed to meet the specific needs of a website redesign. A mega menu approach was desired as the site is a library and thus offers a variety of services that need to be discoverable by users without having to do deep diving. The current menu structure reflects a draft of the proposed site "structure."
The mega menu design request specified the following features:
The overall structure of the mega menu is a collection of basic disclosure patterns consisting of unordered lists and buttons.
<h2 id="main-nav-heading" class="offscreen">
<nav class="mega-menu" aria-labelledby="main-nav-heading" data-menu-state="closed" data-responsive-width="900px">
<button class="responsive-toggle" aria-expanded="false">
<svg focusable="false" ><use xlink:href="#icon-menu"></use></svg>
Menu
</button>
<ul>
<li>
<button aria-expanded="false" class="mega-menu-toggle">
Services and Support
<svg focusable="false" class="icon down"><use xlink:href="#icon-down-triangle"></use></svg>
<svg focusable="false" class="icon up"><use xlink:href="#icon-up-triangle"></use></svg>
</button>
<div class="mega-sub-menu" tabindex="-1">
<ul>
<li class="image-column">
<a href="">
<img src="..." alt="..."/>
<div>Image Link Heading</div>
</a>
</li>
<li class="text-column">
<div class="mega-sub-heading">Optional list heading</div>
<ul class="mega-menu-bulleted">
<li><a href="...">Link text<a></li>
...
<ul>
</li>
<li class="text-column double">
<div class="mega-sub-heading">Optional list heading</div>
<ul class="mega-menu-bulleted">
<li><a href="...">Link text<a></li>
...
<ul>
</li>
</ul>
</div><!-- End sub-menu -->
</li>
<li>
<button aria-expanded="false" class="mega-menu-toggle">...</button>
<div class="mega-sub-menu" tabindex="-1">...</div>
</li>
...
</ul>
</nav>
Within a sub-menu, there are four equal-width columns (25%) available. Columns can involve an image or text. Text columns can also be marked as a double column in that the list of items will be displayed across two columns (50% of the width). CSS is used to prevent individual list items from breaking across the columns.
The mega menu is a collection of drop down menus for navigation. As such, each dropdown should thus mostly follow the recommended practices for the WAI-ARIA disclosure pattern. The example provided for navigation menu examples via disclosures can be used. In terms of accessibility features, the following subsections discuss what is needed for proper implementation.
Importantly, note that this is NOT an application
menu and that pattern should not be used. Application menus are for
web applications such as Google Docs. This is for navigation between
pages on the web. As such is does NOT use the
ARIA roles of either menu
or
menuitem
.
The design HTML for this mega menu adds the requirement
for the dropdown menus to open on hover interaction when in
desktop mode. To support only having hover in desktop mode,
the <nav>
that is
the mega menu should set the attributes
data-responsive-width
to
the minimum width (in pixels) that the desktop mode occurs. The
mega-menu.js code utilizes this value when determining
interactivity for any event. Do not include units when
assigning the attribute.
To support accessibility, the desktop view must properly balance the interactions between opening the menus via hover or direct manipulation. To accomplish this, the mega menu should be considered as existing in one of three states:
The accompanying CSS and JS files track this state
in the attribute data-menu-state
in the <nav>
element for
the mega menu. In general, focus takes priority over the hover state.
Once the menu enters the focus state, hover has no effect until the
menu returns to the closed state. The following table describes the
various state transitions the desktop mega menu should take.
Current State | Interaction | Result |
---|---|---|
Closed | Cursor hovers over a toggle button |
State changes to Hover
Set Associated sub-menu is shown |
Closed | A toggle button receives a click event |
State changes to Focus
Set Associated sub-menu is shown |
Hover | Cursor moves from toggle button to opened sub-menu or opened sub-menu to its associated toggle button. |
No changes |
Hover | Cursor moves from opened sub-menu or its associated toggle button to a different toggle button |
State remains Hover
Set
Set |
Hover |
The toggle button with aria-expanded="true"
experiences a click event
|
State changes to Focus No other changes |
Hover | A link in the opened sub-menu receives focus |
State changes to Focus No other changes |
Focus |
User clicks on a non-interactive part of the opened sub-menu |
State changes to Focus Focus is moved to the opened sub-menu's toggle button |
Hover | Escape key is pressed |
State changes to Closed
The relevant toggle buttons sets The opened sub menus closes |
Focus | Escape key is pressed |
State changes to Closed
All toggle buttons set Any opened sub menus disappear If focus was within a sub-menu, focus is moved to the toggle button associated with the sub-menu |
Focus | A toggle button or opened sub-menu is hovered over |
No changes |
Focus |
The toggle button with aria-expanded="true"
receives a click event
|
State changes to Closed Toggle button sets Opened sub-menu is closed |
Focus |
A toggle button with aria-expanded="false
receives a click event
|
State remains Focus
Set
Set |
Focus |
Focus moves to any part of the mega menu: links in an opened sub-menu or any toggle button. |
No changes |
Focus |
Focus moves outside of the mega menu |
State changes to Closed Toggle button sets Opened sub-menu is closed |
Focus |
User clicks on a non-interactive part of the opened sub-menu. |
State remains Focus Sub-menu remains open Focus remains where it was |
Focus |
User clicks outside of the opened sub-menu on a non-interactive element. |
State changes to Closed Toggle button sets Opened sub-menu is closed Focus is moved to the browser's default |
Focus |
User moves to a different browser tab or application. |
State remains Focus Sub-menu remains open Focus remains where it was |
For keyboard support, the mega menu should provide the following interactivity in both mobile and desktop settings:
Key | Function |
---|---|
Tab or Shift + Tab |
Move keyboard focus among top-level buttons, and if a dropdown is open, move the focus into and through the links in the dropdown. |
Space | If focus is on a disclosure button, activates the button and toggles the visibility of the dropdown. |
Enter |
If focus is on a disclosure button, activates the button and toggles the visibility of the dropdown. If focus is on a link, activates the focused link. |
Escape | If a dropdown is open, closes it. If the focus was within the opened dropdown, set focus on the button that controls that dropdown. |
Although the disclosure pattern lists the arrow keys, Home, and End keys as optional means of interactions, we do not recommend their use here. These interaction options are not implemented consistently. If they are to be added, we would need to make sure ALL disclosures on library sites also provide them.
Additionally, space and enter natively triggers
the click event for buttons, and enter natively triggers
click for links (i.e., <a>
).
No keyboard event listeners are needed for them.
In terms of ARIA support, the only required bit of ARIA is
aria-expanded="true/false"
on the toggle buttons for the dropdown menus and for opening the
responsive mode menu button. For each such
button, aria-expanded
should
be set to false if the associated dropdown menu is closed.
Whenever the associated menu is opened, whether by hover or click,
the value should be set to true.
The disclosure pattern does suggest two additional ARIA attributes:
aria-controls
and
aria-current
but neither are
recommended here. The first, aria-controls
,
is not widely supported by screen readers currently due to difficulties
in implementation. Current recommended practice based on testing suggests
that aria-controls
either provides
no benefit or causes additional problems and should thus be avoided.
aria-current
is well supported among
screen readers currently. Setting aria-current="page"
on a link will inform screen readers that the link is to the page the browser
is currently at. CSS can then be used to indicate that as well to sighted users.
Its utility, however, is debatable in a dropdown menu situation as no indication
is provided in the toggle buttons. Therefore, adding the code to support and properly
update its usage is not required or recommended for this mega menu in either desktop
or responsive mode.
In addition to the above items, the following features are also provided to enhance the accessibility experience:
data-menu-state
changes to focus, the toggle button for the responsive menu (<button class="responsive-toggle">
)
has its attribute of aria-expanded
set to
"true"
.
This means that if the window is resized and the menu switches to responsive mode,
the same sub-menu remains open and available.
:before
and
:after
in CSS on the sub-menus.
Much thanks go to the many fruitful conversations and feedback found on the Web A11Y slack. This project also makes use of the open source hoverintent library.