abaabil

Toolbar

20th lightest of the twenty-three, by js weight

The W3C APG toolbar pattern. Its whole purpose is the tab sequence.

Example (a11y tier)

Tab into it once, then use the arrow keys. Tab again and you leave the toolbar entirely rather than walking the rest of it.

Import

import Toolbar from 'abaabil/toolbar'         // structure only
import Toolbar from 'abaabil/toolbar/styled'  // + CSS
import Toolbar from 'abaabil/toolbar/a11y'    // + role, roving tabindex, arrows

One stop instead of eight

A row of eight buttons is eight stops on the way to whatever comes after it. As a toolbar it is one, and the arrow keys move between the controls inside. That is the entire reason the pattern exists, and a toolbar that does not do it is a <div> with a role on it, which is worse than nothing: it announces a widget and then behaves like plain content.

Children, not an items array

Tabs and menu take an items array because their contents are uniform. A toolbar's are not: buttons, toggles, a select, a separator, whatever the job needs. So this takes children, and manages tabindex on its focusable descendants directly.

Cloning the children would have been the more React-shaped answer and does not work here, because it only reaches the top level and toolbar contents are often wrapped a level or two deep. The DOM is the source of truth for what is focusable instead, re-read on every keystroke so that controls appearing, disappearing or becoming disabled are picked up without anything having to announce it.

Controls that need the arrows keep them

A <select> or a text field inside a toolbar would be unusable if every arrow press moved focus out of it, so those keep their own arrow behaviour. A separator is an <hr>, which is the semantic way to say “these are different groups” and is turned on its side by CSS.

Props

PropTypeDefaultTierDescription
orientation'horizontal' | 'vertical''horizontal'allChooses the arrow pair and sets aria-orientation.
classNamestringallMerged with the base class.
labelstringa11yAccessible name. An unnamed toolbar is announced as “toolbar” and nothing else.
labelledBystringa11yId of an element naming it.