Tab Menu Horizontal
How the Tab Menu Horizontal works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.
The Tab Menu Horizontal is a WAI-ARIA tablist: one tab stop, the arrow keys move between tabs, and moving selects. The optional compact button opens every tab as a menu. It is held to WCAG 2.2 AA for contrast, focus and target size.
Keyboard#
Moves focus to the selected tab, then on to its panel. Only the selected tab is in the tab order.
Moves focus back from the panel to the selected tab.
Moves to the next tab and selects it, wrapping from last to first.
Moves to the previous tab and selects it, wrapping from first to last.
Selects the first tab.
Selects the last tab.
On the compact button, opens the menu with the selected tab focused. In the menu, selects the focused tab and returns focus to it.
In the menu, moves to the next tab, wrapping. Up moves back.
Closes the menu and returns focus to the compact button.
Semantics#
role="tablist"With aria-orientation="horizontal". Give it a name with aria-label when the page has more than one tablist.
Alwaysrole="tab"A <button> with the tab role. Its badge and glyphs are inside the button, so a count is read with the label.
Each tabaria-selectedtrue on the active tab only. The blue label and bar only echo it.
Each tabaria-controlsPoints at the tabpanel the strip switches. Pass panelId.
Each tabrole="tabpanel"Labelled by the selected tab with aria-labelledby, and given tabindex="0" when it has nothing focusable inside.
Paneltabindex="0" / "-1"Only the selected tab is in the tab order.
Rovingaria-haspopup="menu"With aria-expanded, and named More. The menu's items are menuitemradio, checked on the selected tab.
Compact buttonColour contrast#
Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.
Tab label · Default, Hover, Active
The 14px Medium label against the white strip in every state. The compact menu's options are measured the same way, with their 4% hover wash.
WCAG 1.4.3 Contrast (Minimum) ↗Active bar · Active
The 2px (or 8px Thick) #005cf8 bar against the white strip and its #f2f2f2 hairline: 5.40:1 and 4.83:1.
WCAG 1.4.11 Non-text Contrast ↗Selected state · Active
The active tab is blue and also carries the bar, a shape the other tabs don't have, so selection doesn't rest on hue.
WCAG 1.4.1 Use of Color ↗Count badge · With a count
White 12px numbers on #ec0d33 measure 4.49:1, just under 4.5:1.
WCAG 1.4.3 Contrast (Minimum) ↗Focus indicator · Focus visible
A 2px #1a6ef8 ring with a white gap around the tab, 4.54:1 against white.
WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗Measured contrast#
Text against the white strip or menu in each state, with the menu's hover wash laid over white; the count badge against its red fill. 3 of 9 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.
Motion#
- The label colour changes over 150ms and at once under prefers-reduced-motion.
- The bar moves to the new tab without sliding, and the compact button's chevron flips without a transition.
Checklist#
Wire each tab to its panel: pass panelId, and label the panel with aria-labelledby pointing at the selected tab's id rather than repeating the text in aria-label.
Selection follows focus. That suits panels that are already on the page; if a panel loads slowly, select on Enter or Space instead (manual activation), which the preview does not offer yet.
The count badge is 4.49:1, a hair under 4.5:1. Darken the red to #b30a26 or below before the code library ships.
Tabs are 20px tall with a 24px hit area, the WCAG 2.5.8 minimum; the 12px gap between them gives the spacing. The compact button is 24 × 24px.
Narrower than its tabs, the strip wraps onto a second row rather than hiding tabs off the edge. Keep it that way.
Use links in a <nav>, not tabs, when each label goes to a different page.
