@wanjapflueger/a11y-menu
v2.5.2
Published
A fully accessible menu
Downloads
95
Maintainers
Readme
a11y-menu
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.
Table of contents
Requirements
- You need to be able to import or require JavaScript files
Changelog
See changelog.md.
Install
npm i @wanjapflueger/a11y-menu
Example
example.barrierefrei.kiwi/a11y-menu/
Usage
Init
Import class Menu
.
import { Menu } from '@wanjapflueger/a11y-menu';
// var a11yMenu = require("@wanjapflueger/a11y-menu")
Create a new instance
const myMenu = new Menu();
// const myMenu = new a11yMenu.Menu();
Methods
Method Create
Create a navigation of this simple structure.
<nav> <ul> <li><a href="/">Home</a></li> <li> <span>Topics</span> <ul> <li><a href="/dogs">Dogs</a></li> <li><a href="/cats">Cats</a></li> <li><a href="/birds">Birds</a></li> </ul> </li> <li><a href="/contact">Contact</a></li> <li><a href="/privacy">Privacy</a></li> </ul> </nav>
Important:
Each
HTMLLIElement
including a direct child (HTMLUListElement
, submenu)HTMLSpanElement
.It is not possible to create a link on a
HTMLLIElement
that includes a direct child (HTMLUListElement
, submenu)<!-- ✅ Do --> <li> <span>Topics</span> <ul> <li><a href="/dogs">Dogs</a></li> <li>...</li> </ul> </li> <!-- ❌ Do not --> <li> <a href="/topics">Topics</a> <ul> <li><a href="/dogs">Dogs</a></li> <li>...</li> </ul> </li>
Assign that navigation element with JavaScript to a variable, initiate a new instance of
Menu
and call methodCreate
.import { Menu } from '@wanjapflueger/a11y-menu'; const myMenu = new Menu(); myMenu.Create({ nav: document.querySelector('nav'), });
Parameters
See MenuParameters
in src/index.ts.
| Name | Required | Type | Default value | Description | Example |
| -------------------------------------------- | -------- | ------------------- | ------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| nav
| ✅ Yes | HTMLElement
| | Navigation element. Any HTMLElement
being a <nav>
element. | <nav>...</nav>
|
| config
| ❌ No | Config
| undefined
| Menu configuration. | { responsive: [{minWidth: 768, config: [{closeOnNavigate: true, direction: 'x', level: 0}]}] }
|
Config
import { Menu } from '@wanjapflueger/a11y-menu';
const myMenu = new Menu();
myMenu.Create({
nav: document.querySelector('nav'),
config: {
breakpoints: [
{
minWidth: 0,
config: [
{
closeOnNavigate: false,
level: 0,
},
],
},
{
minWidth: 768,
config: [
{
closeOnNavigate: false,
closeOnClick: true,
direction: 'vertical',
level: 0,
},
],
},
{
minWidth: 1280,
config: [
{
direction: 'horizontal',
level: 0,
},
{
direction: 'vertical',
level: 1,
},
],
},
],
},
});
This would mean:
On screens bigger than
768px
and smaller than1280px
...- on
MenuItem.level === 0
use the up- and down arrow keys to go to the nextMenuItem
- on
On screens bigger than
1280px
...On
MenuItem.level === 0
use the left- and right keys to go to the nextMenuItem
On
MenuItem.level === 1
use the up- and down keys to go to the nextMenuItem
The default value for direction
is always 'vertical'
.
Styles
The menu does not come with any design or theme applied.
Selectors
To apply styles for different states, use these selectors:
[role='menubar'] {
/** Top level menu */
}
[role='menu'] {
/** Sub menus */
}
[role='none'] {
/** Menu item parent with no function. Only when the [role='menuitem'] is an anchorlink element */
}
a[role='menuitem'] {
/** Menu item that is clickable */
}
li[role='menuitem'] {
/** Menu item that contains a submenu ([role="menu"]) */
}
[role='menuitem'][aria-expanded='true'] {
/** Menu items that are expanded */
}
[role='menuitem'][data-focus='true'] {
/** The menu item that has the focus */
}
[role='menuitem'][aria-current='page'] {
/** Menu items that match the current page */
}
[role='menuitem'][data-current-parent='true'] {
/** Menu items that are parents of the [aria-current="page"] */
}
[role='menuitem'][data-level='{number}'] {
/** The depth level of a menu item (replace `{number}` with a number, starting with `0`) */
}
Example Styles
Here are some example styles.
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
[role='menubar'] {
display: flex;
}
[role='menuitem'] {
position: relative;
background-color: #ccc;
min-width: 100px;
display: block;
cursor: pointer;
padding: 5px 10px;
}
[role='menuitem']:hover,
[role='menuitem']:focus-visible {
text-decoration: underline;
}
[role='menuitem'] > ul {
position: absolute;
}
[aria-expanded='false']::before {
content: '→';
}
[aria-expanded='true']::before {
content: '↓';
}
[aria-expanded='false'] > ul {
display: none;
}
nav > ul > li > ul > li ul {
left: 100%;
top: 0;
outline: 1px solid red;
}
Keyboard support
In index.ts at menuItemControls several keys have been assigned a function. The keyboard support starts when any element in the menu is active. A menu is active when:
- any
nav [role="menuitem"]
is focussed
| Key | Description |
| ------------ | ------------------------------------------------- |
| ArrowRight
| Next or submenu (direction === 'horizontal'
) |
| ArrowDown
| Next or submenu (direction === 'vertical'
) |
| ArrowLeft
| Previous or parent (direction === 'horizontal'
) |
| ArrowUp
| Previous or parent (direction === 'vertical'
) |
Accessibility Compliance Report
WCAG Level: AAA [^1]
| Browser | Platform | Screen reader | Passed | | ----------------- | ------------- | ---------------------------------------------------------------------------- | ------ | | Chrome 95 | Windows 10 | NVDA | ✅ | | Firefox 93 | Windows 10 | NVDA | ✅ | | Microsoft Edge 95 | Windows 10 | NVDA | ✅ | | Chrome 94 | Android 10 | TalkBack | ✅ | | Chrome 95 | MacOS 11.15.2 | VoiceOver | ✅ | | Firefox 90 | MacOS 11.15.2 | VoiceOver | ✅ | | Microsoft Edge 95 | MacOS 11.15.2 | VoiceOver | ✅ |
[^1]: This information refers only to the technical aspects of the component, not to the design or the editorial handling of any content.