@barrierefrei/a11y-menu-2
v1.2.0
Published
A fully accessible menu
Downloads
12
Maintainers
Readme
a11y-menu-2
A menu that is fully accessible. It has no design or theme applied. That way you can be certain the menu is usable and accessible and you can apply your own styles.
The code is based on the recommendations made by the W3C and has some extra features.
Key features:
Table of contents
Requirements
- You need to be able to import JavaScript files
- Valid HTML structure for a navigation (see Example)
Changelog
See changelog.md.
Install
npm i @barrierefrei/a11y-menu-2
Usage
HTML
The following example contains all nessessary attributes like aria-expanded
, aria-controls
, inert
and aria-label
.
HTML example file: docs/markup.html
The script will automatically add the aria-current="page"
attribute to any link, that matches the current URL. It will also add data-current-parent
to any parent HTMLLiElement
s.
JavaScript
import { A11yMenu2 } from '@barrierefrei/a11y-menu-2';
const myMenu = new A11yMenu2({
nav: document.querySelector('nav'),
});
You can also call a method closeAll
to close all menu items. You could run this on any Event
.:
/** Close all menu item on a button click */
document.getElementById('close')?.addEventListener('click', () => {
myMenu.closeAll();
});
Also check out Events.
Parameters
See ParameterOptions
in src/index.ts.
State changes
State changes are mapped with ARIA.
Buttons that control submenus will change their aria-expanded
attribute and the inert
attribute of the associated submenu.
Example of a collapsed submenu:
<button aria-expanded="false" aria-controls="submenu">Toggle</button>
<div id="submenu" inert>Submenu</div>
Example of an expanded submenu:
<button aria-expanded="true" aria-controls="submenu">Toggle</button>
<div id="submenu">Submenu</div>
Events
Some custom events will be fired. Checkout all available events at src/_events.ts
const myMenu = new A11yMenu2({ ... })
myMenu.onEvent('onSubmenuOpen', (options) => {
console.log('A submenu was opened', options);
console.log('The trigger is:', options.currentTrigger);
console.log('The submenu is:', options.currentSubmenu);
console.log('The navigation is:', options.nav);
})
Keyboard support
| Key | Function |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| Tab / Shift + Tab | Move keyboard focus among top-level buttons and if a submenu is open, into and through links in the submenu. |
| Escape | If a submenu is open, closes it and sets focus on the button that controls that submenu. |
| Home | If focus is on a link/button, and it is not the first link/button, moves focus to the first link/button (toplevel). |
| End | If focus is on a link/button, and it is not the last link/button, moves focus to the last link/button (toplevel). |
|Space or Enter|If focus is on a button that triggers a submenu, activates the button, which toggles the visibility of the submenu (inert
). If focus is on a link: 1.) If any link has aria-current set, removes it. 2.) Sets aria-current="page"
on the focused link. 3.) Activates the focused link (aria-expanded="true"
).|
Styles
The menu does not come with any design or theme applied.
Selectors
To apply styles for different states, use these CSS selectors:
CSS example file: docs/selectors.css
Motivation
This 📦 package is a variant of another accessible menu. While you can navigate @wanjapflueger/a11y-menu with the arrow keys, this 📦 package @barrierefrei/a11y-menu-2 can be navigated with the tab keys.
The reason for this is, that with @wanjapflueger/a11y-menu complex HTML that is not part of the menu structure will break the code, because it relies on a perfekt structure.
This approach is more open. The HTML may contains unordered lists <ul>
that do not contain any menu items. You can even include heading tags like <h2>
or images <img>
or use a <div>
as a submenu item.
In this approach you set a few required HTML-Attributes manually, allowing you to use anchorlinks <a>
, lists <ul>
, <li>
and other tags inside of the surrounding navigation <nav>
that are not part of the very same.
In the examples below we compare @wanjapflueger/a11y-menu and @barrierefrei/a11y-menu-2.
Example A) Simple structure with no extra HTML. Every element is part of the menu structure.
| @wanjapflueger/a11y-menu | @barrierefrei/a11y-menu-2 | | ------------------------ | ------------------------- | | ✅ Supported | ✅ Supported |
<nav>
<ul>
<li>
<span>Colors</span>
<ul>
<li>
<a href="/colors/red">Red</a>
<a href="/colors/blue">Blue</a>
</li>
</ul>
</li>
</ul>
</nav>
Example B) In this example we add a <div class="info">
with a <h2>
and a link <a>
, that is not a menu item. Those elements do nothing for the menu functionality.
| @wanjapflueger/a11y-menu | @barrierefrei/a11y-menu-2 | | ---------------------------------------------------------- | ------------------------- | | ❌ Not supported. Additional HTML elements break the menu. | ✅ Supported |
<nav>
<ul>
<li>
<div class="info">
<h2>Colors</h2>
<a href="/colors">Read more about colors</a>
<ul>
<li>
<a href="/colors/red">Red</a>
<a href="/colors/blue">Blue</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
Accessibility Compliance Report
WCAG Level: AA [^1]
| Browser | Platform | Screen reader | Passed | | ------------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------- | ------ | | Chrome 115 | MacOS 13.0 | VoiceOver | ✅ | | Chrome 115 | Windows 11 | Narrator | ✅ | | Chrome 115 | Android 10 | TalkBack | ✅ | | Firefox 116 | MacOS 13.0 | VoiceOver | ✅ | | Safari 16.1 | MacOS 13.0 | VoiceOver | ✅ | | Microsoft Edge 115 | MacOS 13.0 | VoiceOver | ✅ | | Microsoft Edge 115 | Windows 11 | Narrator | ✅ |
[^1]: This information refers only to the technical aspects of the component, not to the design or the editorial handling of any content.