@barrierefrei/a11y-menu-2
TypeScript icon, indicating that this package has built-in type declarations

1.2.0 • Public • Published

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.

Pipeline status npm

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 HTMLLiElements.

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.

Package Sidebar

Install

npm i @barrierefrei/a11y-menu-2

Weekly Downloads

5

Version

1.2.0

License

ISC

Unpacked Size

42.9 kB

Total Files

8

Last publish

Collaborators

  • wanjapflueger