Tablist
UI pattern
A tablist is a set of tabs, each of which displays one section of content, known as a tab panel. Selecting a tab displays its panel and hides the others, so users see one panel at a time. The tablist and its panels together are called a tabbed interface, or simply tabs.
A tabbed interface has three parts:
- A container with the
tablistrole that holds the tabs. - A tab for each panel, which labels the panel and displays it when selected.
- A tab panel for each tab, which holds that tab's content.
A tablist is horizontal by default and usually sits directly above the displayed panel. A vertical tablist usually sits beside it.
Tabs are activated in one of two ways. With automatic activation, moving focus to a tab also selects it and displays its panel. With manual activation, users move focus among the tabs with the arrow keys and press Enter or Space to select one.
Tabs are one of a few ways to let users choose which part of the content the page presents, and they are included in Related Patterns. Some components can be styled to look like tabs but serve a different purpose: site navigation holds links that move to other pages, and a menu bar holds commands. Neither chooses which content the page presents.
When to Use
Use a tablist when:
- Content divides into sections at the same level that users read one at a time, such as a product's description, specifications, and reviews.
- Users benefit from seeing every section's name at once and switching between sections without leaving the page.
- Space is limited and displaying every section at once would make the page long and hard to scan.
Do not use a tablist when:
- Selecting an item loads another page or changes the URL. Use a list of links in a navigation landmark, even when the links are styled to look like tabs.
- Users need to compare sections side by side or read them in sequence. Show the sections on the page under headings, or use an accordion that can open several at once.
- The sections are steps in a sequential process rather than independent topics. Use a pattern built for a sequence, such as a multi-step form.
- There are so many sections that the tabs wrap onto a second row or scroll out of view. Present every section on the page under its own heading, after a table of contents that links to each heading.
Examples
Code in this section is meant to demonstrate techniques rather than requirements and lacks features needed in a production application.
HTML has no tab elements, so a tabbed interface is always built with ARIA roles and code. The example shows a horizontal tablist of orchestra instruments with four tabs and their panels, and manages keyboard focus with a roving tabindex. Managing focus with aria-activedescendant instead gives a tablist no advantage.
Tabs With Roving Tabindex
The tablist takes its accessible name from the heading through aria-labelledby. Each tab is a <button> with role="tab", so it's focusable and fires a click on Enter and Space without extra code. Each tab points aria-controls at its panel, each panel points aria-labelledby back at its tab for its name, and aria-selected="true" marks the selected tab.
Only the selected tab has tabindex="0", which makes the tablist a single stop in the tab order. The hidden attribute hides the other panels from every user and takes them out of the tab order. The displayed panel's content has no focusable element, so its tabindex="0" makes it the next stop after the tablist.
Roving tabindex HTML
<h2 id="instruments-heading">Orchestra instruments</h2>
<div
role="tablist"
aria-labelledby="instruments-heading">
<button
type="button"
role="tab"
id="violin-tab"
aria-selected="true"
aria-controls="violin-panel"
tabindex="0">
Violin
</button>
<button
type="button"
role="tab"
id="viola-tab"
aria-selected="false"
aria-controls="viola-panel"
tabindex="-1">
Viola
</button>
<button
type="button"
role="tab"
id="cello-tab"
aria-selected="false"
aria-controls="cello-panel"
tabindex="-1">
Cello
</button>
<button
type="button"
role="tab"
id="bass-tab"
aria-selected="false"
aria-controls="bass-panel"
tabindex="-1">
Bass
</button>
</div>
<div
role="tabpanel"
id="violin-panel"
aria-labelledby="violin-tab"
tabindex="0">
...
</div>
<div
role="tabpanel"
id="viola-panel"
aria-labelledby="viola-tab"
tabindex="0"
hidden>
...
</div>
<div
role="tabpanel"
id="cello-panel"
aria-labelledby="cello-tab"
tabindex="0"
hidden>
...
</div>
<div
role="tabpanel"
id="bass-panel"
aria-labelledby="bass-tab"
tabindex="0"
hidden>
...
</div>For brevity, the following omits disabled tabs and error checks. It uses automatic activation, so moving focus with the arrow keys also selects the tab. For manual activation, move focus without selecting, and let the click that a <button> fires on Enter and Space select the tab.
Roving tabindex JavaScript
class Tablist {
constructor(tablist) {
this.tablist = tablist;
this.vertical = tablist.getAttribute("aria-orientation") === "vertical";
this.tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
this.panels = this.tabs.map((tab) =>
document.getElementById(tab.getAttribute("aria-controls"))
);
tablist.addEventListener("keydown", (event) => this.onKeydown(event));
tablist.addEventListener("click", (event) => this.onClick(event));
}
// Select a tab, display its panel, and move the tab stop to it
select(tab) {
this.tabs.forEach((item, index) => {
const selected = item === tab;
item.setAttribute("aria-selected", String(selected));
item.tabIndex = selected ? 0 : -1;
this.panels[index].hidden = !selected;
});
tab.focus();
}
onKeydown(event) {
const index = this.tabs.indexOf(document.activeElement);
const last = this.tabs.length - 1;
const nextKey = this.vertical ? "ArrowDown" : "ArrowRight";
const previousKey = this.vertical ? "ArrowUp" : "ArrowLeft";
let next;
switch (event.key) {
case nextKey:
next = index >= last ? 0 : index + 1;
break;
case previousKey:
next = index <= 0 ? last : index - 1;
break;
case "Home":
next = 0;
break;
case "End":
next = last;
break;
default:
return;
}
event.preventDefault();
this.select(this.tabs[next]);
}
onClick(event) {
const tab = event.target.closest('[role="tab"]');
if (tab) {
this.select(tab);
}
}
}
const instrumentTabs = new Tablist(document.querySelector('[role="tablist"]'));Structure and Semantics
A tabbed interface conveys its structure through three roles. A container with the tablist role holds elements with the tab role, and each tab controls an element with the tabpanel role. Assistive technology uses that structure to report how many tabs there are, which one is selected, and which panel belongs to which tab.
No HTML element carries these roles, so every part of a tabbed interface needs ARIA.
The tablist is the container that holds the tabs:
| Attribute | How to use |
|---|---|
role | Set to "tablist" on the element that directly contains the tabs. |
aria-labelledby or aria-label | Name the tablist after what its tabs are about. |
aria-orientation | Set to "vertical" on a vertical tablist. The default is horizontal. |
Each tab names a panel and displays it when selected:
| Attribute | How to use |
|---|---|
role | Set to "tab" on each tab. |
aria-selected | Set to "true" on the selected tab and "false" on every other tab. |
aria-controls | Set to the id of the tab's panel. |
tabindex | Set to "0" on the selected tab and "-1" on the others. |
aria-disabled | Set to "true" on a tab that can't be selected. |
Each tab panel holds the content of one tab:
| Attribute | How to use |
|---|---|
role | Set to "tabpanel" on each panel. |
aria-labelledby | Set to the id of the panel's tab. |
tabindex | Set to "0" on the displayed panel unless its first element with content is focusable. |
The tab role makes its content presentational, so a tab is exposed only as its accessible name. A tablist holds only tabs, so the panels sit outside it, usually right after it.
Tablist Has Tabs
A tablist contains elements with the tab role and nothing else, and at least one of its tabs can be selected. When a <ul> holds the tabs, give each <li> the tab role, or give each <li> role="presentation" and put the tab role on the element inside it.
Leaves at least one tab operable
Reported as: No active tabs
A tablist must have at least one tab that isn't disabled.
When every tab is disabled, keyboard users reach the tablist and find nothing that takes focus or responds to the arrow keys. The panels and their content are out of reach, and nothing says why.
Keep at least one tab available, preferably the one whose content is ready. When no panel has content yet, show what users are waiting for in place of the tablist.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Gives each tab the tab role
Reported as: No group members with required role
Each tab in a tablist must have the tab role.
When the tablist's children are generic elements or buttons without role="tab", or extra wrappers sit between the tablist and its tabs, assistive technology finds no tabs to report. Screen reader users are told they've reached a tab list, then hear its contents with no count or selection.
Give each tab role="tab" and make it a child of the tablist. Remove any wrapper between them, or give the wrapper role="presentation".
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Admits only the expected roles
Reported as: Unexpected interactive group members
Every child a tablist exposes must have the tab role.
When a button, link, or badge sits in the tablist beside the tabs, such as an "Add tab" button, assistive technology can count it as part of the set, so the positions users hear no longer match the tabs on screen. The arrow keys can also stop on something that isn't a tab.
Move a control that acts on the whole tablist to just outside it. Take decorative elements out of the accessibility tree with aria-hidden="true".
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Role
Each part of a tabbed interface has its own role. The tablist role goes on the element that directly contains the tabs, whether that's a <div> or a <ul>, and tells assistive technology that the tabs inside it belong to one set. Each tab has the tab role, as Tablist Has Tabs describes, and each panel the tabpanel role, as Tab Panels Roles describes.
Has the tablist role
Reported as: Element has incorrect role
The container that holds a set of tabs must have the tablist role.
When the tabs sit in a generic <div> or a plain list, assistive technology doesn't expose them as one set. Screen reader users aren't told how many tabs there are or where they are among them.
Add role="tablist" to the element that directly contains the tabs.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Puts the tablist role on the tabs' parent
Reported as: Incorrect root element
Sanity check. Evinced checks that the tablist role sits on the element that directly contains the tabs. This check runs on every UI pattern, not only on tablists.
A tablist should carry the tablist role on the element that directly contains the tabs, not on a wrapper around it or on an element inside it.
When the role sits on an outer wrapper while the tabs sit inside another element, the tabs aren't the tablist's children. Assistive technology can't count them or report the position of each one, so screen reader users hear no tab count or position. The element that holds the tab stop may also have no role.
Put role="tablist" on the tabs' parent element, along with its accessible name and aria-orientation. Keep any layout wrapper free of roles and handlers.
Tab Panels Roles
The tabpanel role goes on the element each tab's aria-controls names, and tells assistive technology where that tab's content begins and ends.
Marks what a tab controls as a panel
Reported as: Missing tabpanel role
The element a tab controls must have the tabpanel role.
When the panel is a plain <div>, screen reader users are told a tab is selected and get no indication of where its content starts. The content runs on from the tablist as part of the page, with no named region to move to.
Add role="tabpanel" to the element each tab's aria-controls references, in the code that renders every panel, and point the panel's aria-labelledby at its tab.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Accessibility Label
The tablist has an accessible name that tells users which set of tabs they're in and what it's for. Its tabs and panels are named too, as Tabs Accessibility Labels and Tab Panels Accessibility Labels describe.
The following example names the tablist from its heading, names each tab from the alt text of its image, and names each panel from its tab.
<h2 id="instruments-heading">Orchestra instruments</h2>
<div
role="tablist"
aria-labelledby="instruments-heading">
<button
type="button"
role="tab"
id="violin-tab"
aria-selected="true"
aria-controls="violin-panel"
tabindex="0">
<img src="violin.png" alt="Violin" />
</button>
<button
type="button"
role="tab"
id="viola-tab"
aria-selected="false"
aria-controls="viola-panel"
tabindex="-1">
<img src="viola.png" alt="Viola" />
</button>
</div>
<div
role="tabpanel"
id="violin-panel"
aria-labelledby="violin-tab"
tabindex="0">
...
</div>
<div
role="tabpanel"
id="viola-panel"
aria-labelledby="viola-tab"
tabindex="0"
hidden>
...
</div>Names the tablist
Reported as: Missing contextual labeling
A tablist must have an accessible name that says what its set of tabs is for.
When a tablist has no name, screen reader users hear "tab list" and then the first tab, and learn what the tabs cover only from the tab names. On a page with more than one tablist, they can't tell the sets apart from the tablist alone.
Point aria-labelledby at the heading or other visible text that introduces the tabs. Use aria-label only when there's no visible text to reference, and name the tablist after its subject, not after its kind.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Tabs Accessibility Labels
Every tab has an accessible name. A tab's name is essential: users hear it and say it to choose the tab, and through aria-labelledby it also names the tab's panel.
Names each tab
Reported as: Group members missing accessible names
Every tab must have an accessible name.
When a tab shows only an icon or an image with empty alt text, screen reader users hear "tab, 2 of 4" with nothing to tell the tabs apart. The panel, which takes its name from its tab, is unnamed as well.
Give each tab visible text, or give an image inside it alt text that names the panel. Use aria-label on the tab only when there's no text or image to name it from.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Tells one tab from another
Reported as: Group contains duplicate names
Tabs in the same tablist should have unique accessible names.
When two tabs share a name, such as two "Untitled" tabs in a document editor, screen reader users can't tell which panel each one displays. Voice control users can't select one by name either.
Include the distinguishing text in each name, such as "Untitled 1" and "Untitled 2".
Severity: Serious
Tab Panels Accessibility Labels
Every tab panel has an accessible name, taken from its tab, so users who move into a panel know which tab's content they're in.
Gives each panel an accessible name
Reported as: Tabpanel does not have an accessible name
A tab panel must have an accessible name.
When a panel has only its role, screen reader users who move into it hear an unnamed tab panel. Someone who just changed tabs gets no confirmation, and someone who arrives at a displayed panel can't tell which tab's content they're in.
Set the panel's aria-labelledby to the id of its tab. When that isn't possible, use an aria-label that matches the tab's wording.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Takes its name from its tab
Reported as: Tabpanel is not labeled by its control
A tab panel should take its name from its tab through aria-labelledby, not from a copy of the tab's text or from a heading inside the panel.
When a panel has a hand-written aria-label, its name goes stale as soon as the tab is renamed. When it's labeled by its own heading, nothing ties the panel back to its tab, so users who jump into the content can't tell which tab it belongs to.
Give each tab an id and point the panel's aria-labelledby at it. Keep any heading inside the panel as content.
Severity: Moderate
Set Size
The browser counts the tabs in a tablist when they're all in the DOM. When only some are rendered, set aria-setsize on every tab to the total number of tabs, with the same value on each, so assistive technology can report the size of the full set.
Reports one total for the set
Reported as: Aria-setsize conflicting values
Every tab in a tablist must declare the same aria-setsize.
When the total is written to each tab as it's added and never updated on the others, the count changes as users move through the tabs, from "2 of 4" to "3 of 5". Users can't tell which number is right.
Write the total to every tab from one source, and update it on all of them when a tab is added or closed. When the full size isn't known yet, leave the attribute off.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Carries the total on each item
Reported as: Aria-setsize invalid implementation
A tablist should set aria-setsize on each tab, not on the tablist.
When the count sits only on the tablist, it never reaches assistive technology, which reads the total from the tab that has focus. Users moving through a partly rendered tablist hear no total.
Set aria-setsize on every tab with the same value, along with each tab's aria-posinset, in the same code that renders the tabs.
Severity: Minor
Set Position
The browser works out each tab's position, reported as "2 of 4", when every tab is in the DOM. When only some tabs are rendered, such as in a tablist that adds tabs as it scrolls, set aria-posinset on each tab to its position in the full set. Otherwise, leave it off.
Fits each position in the set
Reported as: Aria-posinset is larger than aria-setsize
A tab's aria-posinset must be a whole number from 1 up to its aria-setsize.
When positions come from an array index, or from the tabs currently rendered rather than the full set, screen reader users hear something that can't be true, such as "tab 5 of 4", and lose track of how many tabs there are.
Derive the position and the size from the same collection, start positions at 1, and recompute both when tabs are added, closed, or reordered.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Carries the position on each item
Reported as: Aria-posinset invalid implementation
A tablist should set aria-posinset on each tab, not on the tablist.
When the attribute sits on the tablist, it has no effect, and the tabs that need a position have none. Users moving through a partly rendered tablist get no sense of where they are in it.
Move aria-posinset onto each tab, numbered by its place in the full set, and set aria-setsize alongside it.
Severity: Minor
Keyboard Navigation Orientation
A tablist is horizontal by default: the tablist role has an implicit aria-orientation of horizontal. A vertical tablist sets aria-orientation="vertical" and answers the Up Arrow and Down Arrow keys, so screen reader users know which keys to press.
Declares the orientation it uses
Reported as: Conflicting aria-orientation
A tablist's aria-orientation must match the arrow keys that move focus among its tabs.
When a vertical tablist leaves aria-orientation at its horizontal default while the Up Arrow and Down Arrow keys move focus, or the reverse, screen reader users press the keys they're told to use and nothing happens. They conclude the tabs have no keyboard support.
Set aria-orientation="vertical" when the Up Arrow and Down Arrow keys move focus, and leave it off or set it to horizontal when the Left Arrow and Right Arrow keys do. Derive the attribute and the key handling from the same state.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Uses a valid aria-orientation value
Reported as: Invalid aria-orientation value
A tablist's aria-orientation should be horizontal or vertical.
When the attribute holds any other value, such as column or an empty string, assistive technology ignores it and treats the tablist as horizontal. Users of a vertical tablist are told to use the Left Arrow and Right Arrow keys, which do nothing.
Write one of the two values, in lowercase, chosen to match the keys that move focus. For a horizontal tablist, you can leave the attribute off.
Severity: Minor
aria-selected
Each tab reports whether it's selected with aria-selected, kept in step with the panel that's displayed. In a tablist that allows one selection, exactly one tab has aria-selected="true", and it's the tab whose panel users see.
Marks which member is selected
Reported as: No selection attributes
Every tab in a tablist must declare its selection state with aria-selected.
When the selected tab is shown only by styling, screen reader users hear each tab's name with no indication of which one is selected. They can't tell which panel is displayed, or whether their choice took effect.
Set aria-selected="true" on the selected tab and "false" on every other tab, from the same state that applies the selected styling and displays the panel.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Leaves native state to HTML
Reported as: Avoid aria-checked on native elements
Tabs built from native inputs, such as radio buttons styled as tabs, must leave their state to checked and not add aria-checked.
When each input also carries aria-checked, the two states are kept separately and can disagree as users change tabs, so a tab can be reported as unchecked while its panel is the one displayed.
Remove aria-checked from the native inputs and let checked report which tab is chosen.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Picks one selection state
Reported as: Mixed selection attributes
A tab should indicate selection with aria-selected only, not with aria-checked as well.
When tabs carry both attributes, the values can drift apart, so a tab can be reported as checked and not selected at the same time. Users can't tell which state reflects the displayed panel.
Remove aria-checked from the tabs and keep aria-selected, the state assistive technology reports for a tab.
Severity: Minor
Disabled
A tab can be disabled when its content isn't available, and a whole tablist can be disabled with aria-disabled="true" on the container. A disabled tab can't be selected, and the selected tab is never disabled, as described in Unreachable Group.
A tab built from a <button> can use the disabled attribute, which also stops the tab from taking focus, so screen reader users moving through the tabs won't find it. aria-disabled keeps a disabled tab in the arrow-key sequence.
Disables only when role allows
Reported as: Component does not support disabled state
A disabled state must be set only on a tablist or a tab, never on a tab panel, whose role has no disabled state.
When a panel carries aria-disabled="true", assistive technology may ignore it or report the panel inaccurately, and the controls inside it stay available. Users still select the tab and reach its content, so the attribute does nothing the design intended.
When a tab's content isn't available, disable the tab instead, and take the attribute off the panel.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Disables only elements that support it
Reported as: Element type does not support disabled state
An element in a tablist must carry disabled or aria-disabled only when its role supports a disabled state.
When the state sits on an element whose role has none, such as a wrapper, a list item or a heading, assistive technology may drop it or report it on something users can't operate, while the control that really is unavailable is still reported as available.
Remove the attribute from that element, and set the disabled state on the control itself, using an element or role that supports it, such as a native <button>.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Avoids conflicting disabled states
Reported as: Conflicting disabled values
A tab must declare its disabled state one way only: the disabled attribute on a <button> with role="tab", or aria-disabled on a tab built from any other element.
When a <button> tab carries aria-disabled instead of disabled, it stays operable while assistive technology reports it as disabled. When both attributes are set and disagree, assistive technology can report a state the browser doesn't enforce. Either way, screen reader users are told the wrong thing about whether they can select the tab.
Use disabled alone on a <button> tab and aria-disabled alone on any other tab or on the tablist, removing aria-disabled or setting it to false when the tab is enabled again.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Uses aria-disabled rather than disabled
Reported as: Element does not support the disabled attribute
A tab built from an element other than a <button> must convey its disabled state with aria-disabled, not the disabled attribute.
When disabled is set on a <div> or <li> with role="tab", the browser ignores it. The tab stays exposed as available, so users are told they can select a tab the design means to be unavailable, even when styling grays it out.
Set aria-disabled="true" instead, and have the code refuse to select the tab while it's set.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Screen Readable Tab Panels
The displayed panel is in the accessibility tree, and the panels that aren't displayed are not, so assistive technology users get the same content as everyone else. The hidden attribute and display: none handle this for every panel, with no ARIA needed.
When a panel has to stay rendered while it's hidden, such as during a transition that fades it out, hide it from assistive technology with aria-hidden="true". Also keep its content out of the tab order, either with the inert attribute or with tabindex="-1" on each focusable element inside it. The following example shows these techniques and three ways they go wrong.
<!-- Hidden, with inert keeping its content out of the tab order -->
<div role="tabpanel" id="viola-panel" aria-labelledby="viola-tab" style="opacity: 0;" aria-hidden="true" inert>...</div>
<!-- Hidden, with each focusable element taken out of the tab order -->
<div role="tabpanel" id="viola-panel" aria-labelledby="viola-tab" style="opacity: 0;" aria-hidden="true">
...
<button type="button" tabindex="-1">Listen</button>
</div>
<!-- Fails: the panel is invisible but still in the accessibility tree -->
<div role="tabpanel" id="viola-panel" aria-labelledby="viola-tab" style="opacity: 0;">...</div>
<!-- Fails: the button can take focus inside a hidden panel -->
<div role="tabpanel" id="viola-panel" aria-labelledby="viola-tab" style="opacity: 0;" aria-hidden="true">
...
<button type="button">Listen</button>
</div>
<!-- Fails: the displayed panel is hidden from assistive technology -->
<div role="tabpanel" id="violin-panel" aria-labelledby="violin-tab" tabindex="0" aria-hidden="true">...</div>Puts the shown panel in the tree
Reported as: Active tab panel not available to assistive technology
The panel of the selected tab must be exposed to assistive technology.
When the displayed panel carries aria-hidden="true", or sits inside an element that does, or when the tab's aria-controls points at a different panel, screen reader users select a tab and hear no content. The tablist seems broken, and the content seems not to exist.
Remove aria-hidden from the displayed panel and its ancestors in the same update that displays it, and point each tab's aria-controls at the panel it displays.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Keeps inactive panels out of the tree
Reported as: Inactive tab panel available to assistive technology
A panel that isn't displayed must be hidden from assistive technology.
When panels are hidden with opacity: 0, zero height, or off-screen positioning, they stay in the accessibility tree. Screen reader users hear the content of every panel, one after another, with nothing to say which belongs to the selected tab.
Hide the panels that aren't displayed with the hidden attribute or display: none, and update them each time the selection changes. When a transition keeps a panel rendered, hide it when the transition ends.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Visible Tab Panels
The panel of the selected tab is displayed, and every other panel is hidden. Hide the panels that aren't displayed from every user at once, so what's on screen, what's in the accessibility tree, and what's in the tab order always agree.
The following example shows the displayed panel and three ways to hide the others from every user. A CSS class that sets the same property works as well.
<!-- The displayed panel -->
<div role="tabpanel" id="violin-panel" aria-labelledby="violin-tab" tabindex="0">...</div>
<!-- Fails: the displayed panel is invisible on screen but still read by screen readers -->
<div role="tabpanel" id="violin-panel" aria-labelledby="violin-tab" tabindex="0" style="opacity: 0;">...</div>
<!-- Panels that aren't displayed, hidden from every user -->
<div role="tabpanel" id="viola-panel" aria-labelledby="viola-tab" hidden>...</div>
<div role="tabpanel" id="cello-panel" aria-labelledby="cello-tab" style="display: none;">...</div>
<div role="tabpanel" id="bass-panel" aria-labelledby="bass-tab" style="visibility: hidden;">...</div>Displays the panel its tab selects
Reported as: Active tab panel not visible
The panel of the selected tab must be displayed on screen.
When a tab's aria-controls names the wrong panel, or a CSS rule keeps the panel hidden, users select a tab and see either nothing below it or another tab's content. They can't tell which has happened, and often conclude the section is empty.
Generate each tab's aria-controls and its panel's id from the same source, and display the panel from the current selection in the same update that hides the others.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Keeps inactive panels off screen
Reported as: Inactive tab panel visible
Only the panel of the selected tab should be displayed.
When every panel renders at once, or the previous panel isn't hidden as a new one is displayed, content from several panels appears together. Users can't tell which content belongs to the selected tab, and people using magnification may not see where one panel ends and the next begins.
Hide every panel except the selected one, with the hidden attribute or display: none, in the same update that displays the new panel.
Severity: Serious
Site Navigation Detected
Selecting a tab displays a panel that's already on the page, without changing the URL. A row of links to other pages is navigation, even when it's styled to look like tabs, and belongs in a navigation landmark, which the Site Navigation UI pattern covers.
Uses links for moving between pages
Reported as: Misused role for site navigation
A tab must display content on the same page rather than load another page.
When activating a tab changes the URL, screen reader users are told a panel will change and find themselves on a new page. They lose their place and anything they'd entered, with no warning. The destinations also don't appear among the page's navigation landmarks.
Build a set of page destinations as a list of links in a <nav> element, which can still be styled to look like tabs. Keep the tablist, tab, and tabpanel roles for content that switches in place.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
Forbidden Interactive Children
Tabs contain no interactive elements. The tab role makes its content presentational, so a close button or link inside a tab loses its role and name, and assistive technology users can't reach it. Tab panels aren't affected: they sit outside the tablist and can hold any content.
Avoids nested interactives
Reported as: Forbidden interactive children; Nested interactive elements
A tab must not contain interactive elements, such as a button, a link, or a checkbox.
When a tab holds a control, such as a close button beside its name, assistive technology may drop the inner control's role or expose only the tab. Users hear one control where two exist, and a click or key press has two plausible targets.
Move the control out of the tab. To let users close tabs, support the Delete key on the focused tab or offer a close command in a context menu, as APG describes.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Relationships
A tablist owns its tabs, which are its DOM children, and each tab controls one panel. The panels sit outside the tablist, usually right after it in the DOM, so the relationship between a tab and its panel is declared in both directions: aria-controls on the tab names its panel, and aria-labelledby on the panel names its tab.
Some screen readers use aria-controls to let users move from a tab straight to its panel.
aria-controls
Each tab has aria-controls set to the id of the one panel it displays. A hidden panel is still in the DOM, so the reference holds whether or not the panel is displayed. When panels render only as their tabs are selected, set the attribute once the panel exists.
Controls a single target
Reported as: Aria-controls has multiple targets
A tab's aria-controls should reference a single panel.
When the attribute lists several ids, the tab claims to control several elements, and screen reader users can't tell which one holds its content.
Point each tab's aria-controls at its own panel. When selecting a tab changes several regions, put them all inside that one panel.
Severity: Minor
References valid aria-controls
Reported as: Invalid aria-controls reference
A tab's aria-controls must reference a panel that exists in the document.
When the id is misspelled, belongs to a panel that was replaced, or names a panel that hasn't rendered yet, assistive technology finds no target. Screen reader users who rely on aria-controls can't move from the tab to its panel.
Generate the tab's aria-controls and the panel's id from the same source, and keep them in step as panels are created or removed.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Sets aria-controls
Reported as: Missing aria-controls attribute
Every tab must have aria-controls set to the id of its panel.
When no attribute connects a tab to its panel, the relationship exists only in the layout. Assistive technology can't convey which content the tab displays, so screen reader users have to search for what changed.
Add aria-controls to each tab, with the unique id of the panel it displays.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
aria-owns
A tablist holds its tabs as its DOM children and needs no aria-owns when they're there. Don't use aria-owns to connect a tab to its panel. Use aria-controls.
Avoids aria-owns
Reported as: Avoid aria-owns
General check. Evinced checks that the tablist and its tabs don't use aria-owns to claim elements elsewhere in the document as their children. This check runs on every UI pattern, not only on tablists.
A tablist should hold its tabs as its DOM children rather than own them with aria-owns, and a tab should own nothing.
When a tablist owns tabs rendered elsewhere, assistive technology that doesn't honor the attribute reports them outside the tablist, so screen reader users get a partial set with the wrong count. They can also hear the tabs in a different order from the one the arrow keys follow. When a tab owns its panel, the panel's content becomes presentational and loses its roles and structure.
Render every tab inside the tablist, in the order the arrow keys move through them. Keep each panel outside its tab, and reference it from the tab with aria-controls.
Owns only conforming roles
Reported as: Invalid aria-owns values
General check. Evinced checks that every element a tablist claims with aria-owns has the tab role. This check runs on several UI patterns.
When a tablist uses aria-owns, each element it references should have the tab role.
When an owned element has another role, the tablist's structure no longer adds up. The tab count and positions come out wrong, and the arrow keys can reach elements that aren't tabs. An element owned by two containers, or two containers that own each other, leaves the structure undefined.
Reference only tabs, and own each tab from one tablist.
Operation
With a pointer, clicking or tapping a tab selects it and displays its panel.
With a keyboard, the tablist is a single stop in the tab order. The Tab key moves focus into the tablist, landing on the selected tab, and the next press moves focus to the displayed panel, or to the first focusable element inside it. Shift + Tab from the tablist moves focus to whatever comes before it. Inside the tablist, the arrow keys move focus among the tabs.
With automatic activation, moving focus to a tab also selects it. APG recommends this when panels display without a noticeable delay, which usually means their content is already loaded. With manual activation, focus moves without changing the selection, and Enter or Space selects the focused tab. Whichever you choose, apply it to every key that moves focus.
Focus moves among the tabs with a roving tabindex, described in the ARIA Authoring Practices Guide on keyboard navigation inside components: the focused tab holds tabindex="0" and the others tabindex="-1".
The following table shows the keys for a horizontal tablist. In a vertical tablist, the Down Arrow and Up Arrow keys take the place of the Right Arrow and Left Arrow keys. Leave the other pair of arrow keys alone, so they keep scrolling the page.
| Key | Function |
|---|---|
| Right Arrow | Moves focus to the next tab or, from the last, to the first, and selects it with automatic activation |
| Left Arrow | Moves focus to the previous tab or, from the first, to the last, and selects it with automatic activation |
| Home | Moves focus to the first tab, and selects it with automatic activation |
| End | Moves focus to the last tab, and selects it with automatic activation |
| Enter or Space | Selects the focused tab with manual activation |
| Delete (optional) | Closes the focused tab and its panel, when tabs can be closed |
Focus Sequence
A tablist is a single stop in the tab order, and that stop lands on the selected tab. The displayed panel is the next stop: give it tabindex="0" unless its first element with content is focusable, and keep the panels that aren't displayed out of the tab order. The example under Examples shows this sequence.
Reachable with the Tab key
Reported as: Composite not in focus sequence
A tablist must have one of its tabs in the tab order.
When every tab has tabindex="-1", or the tabs are generic elements with no tabindex, the Tab key passes over the tablist. Keyboard users can't reach any tab, and the arrow keys only work once a tab has focus.
Set tabindex="0" on the selected tab and tabindex="-1" on the others. Set the tab stop when the tablist first renders.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Lands on the selected item
Reported as: Composite unpredictable focus
When focus enters a tablist, it must land on the selected tab, or on the first tab when none is selected.
When tabindex="0" stays on the first tab after another is selected, screen reader users arriving with the Tab key hear the first tab as though its panel were the one displayed.
Move tabindex="0" to the newly selected tab each time the selection changes, and set tabindex="-1" on the tab that had it.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Keeps inactive panels out of tab order
Reported as: Inactive tab panel focusable
A tab panel that isn't displayed must be out of the tab order.
When panels are hidden with opacity: 0 or off-screen positioning and each carries tabindex="0", the Tab key stops on panels that aren't on screen. The focus indicator disappears from view, and screen reader users hear content from a panel that isn't displayed.
Hide the panels that aren't displayed with the hidden attribute or display: none, which takes them out of the tab order. Otherwise, set tabindex="-1" on them, and update it as the selection changes.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.4.11 Focus Not Obscured (Minimum) (AA) | When a user interface component receives keyboard focus, the component is not entirely hidden due to author-created content. |
Gives its items a single tab stop
Reported as: Redundant focusable children
A tablist must put no more than one tab in the tab order, with the arrow keys reaching the rest.
When every tab is a <button> with no tabindex="-1", or each has tabindex="0", the Tab key stops on every tab. Users press Tab through the whole tablist to reach the panel, and the tabs read as separate controls rather than one set.
Set tabindex="0" on the selected tab and tabindex="-1" on the others, and move the tabindex="0" as the selection changes.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Makes the shown panel reachable
Reported as: Tabpanel not focusable
The displayed panel should be reachable with the Tab key from the tablist.
When the panel holds only text and has no tabindex, the Tab key moves from the tablist past the panel to whatever follows it. Keyboard users can select a tab and never reach its content, and screen reader users can take the panel for empty.
Give the displayed panel tabindex="0", unless its first element with content is already focusable.
Severity: Moderate
Moves Tab from the tab to its panel
Reported as: Focus unexpectedly exited the tabs component
Pressing Tab on the active tab should move focus into its panel.
When focus jumps past the panel, to a later control on the page or to the next tab stop after the tablist, keyboard users who selected a tab can't get to what it shows without searching for it.
Give the active panel tabindex="0" when its first content isn't focusable, keep the inactive panels out of the tab order, and keep the panel right after the tablist in the DOM.
Severity: Moderate
Keyboard Navigation
The arrow keys that match the tablist's orientation move focus from one tab to the next, wrapping at either end, and Home and End move it to the first and last tab. With automatic activation, each move also selects the tab. For code that handles each of these keys, see Tabs With Roving Tabindex.
Keeps aria-activedescendant valid
Reported as: Missing active descendant
When a tablist manages focus with aria-activedescendant rather than a roving tabindex, the attribute must hold the id of the focused tab and change each time focus moves.
When the attribute keeps the value it had at setup, or names an id that no longer exists, the visible highlight moves but screen reader users hear the old tab, or none. They select a tab they aren't on.
Give every tab a unique id, and update aria-activedescendant in the same step that moves the highlight, each time an arrow key, Home, or End is pressed, and on a click.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Moves focus with the arrow keys
Reported as: Focus not updated with arrow keys
The Right Arrow and Left Arrow keys must move focus to the next and previous tab in a horizontal tablist, and the Down Arrow and Up Arrow keys must do so in a vertical one.
When the tabs answer only clicks, an arrow key scrolls the page and focus stays where it was. Keyboard users can't reach any tab but the one that holds the tab stop.
Handle the arrow keys on the tablist, move focus one tab per press, and call preventDefault so the page doesn't scroll.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Keeps focus after selection
Reported as: Focus management on tab select
Selecting a tab must leave focus on that tab or move it to the panel the tab displays.
When selecting with Space re-renders the tablist, the focused element is replaced and focus falls back to the top of the page. Screen reader users lose their place and don't hear the panel that just appeared.
Keep focus on the selected tab, or move it to the panel when that's the natural next step. When selection re-renders the tabs, restore focus once the new markup is in place.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.2 Meaningful Sequence (A) | When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined. |
| EN 301 549 | 9.1.3.2 Meaningful Sequence | Same as the WCAG text. |
Moves focus with Home and End
Reported as: Focus not updated with Home or End keys
The Home key must move focus to the first tab and the End key to the last.
When only the arrow keys are handled, Home and End scroll the page instead, and reaching the last tab takes one key press per tab, each announced by a screen reader.
Handle Home and End on the tablist, move focus the same way the arrow keys do, and call preventDefault so the page doesn't scroll.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Wraps focus at the ends
Reported as: Arrow key navigation loop failed
From the last tab, the forward arrow key should move focus to the first tab, and from the first, the backward arrow key should move it to the last.
When focus stops at either end, a key press does nothing, which looks the same as a tablist whose keyboard support has stopped working. Users press again rather than reversing direction.
When focus is on the last tab and the Right Arrow key is pressed, or the Down Arrow key in a vertical tablist, move focus to the first tab, and mirror that from the first tab with the Left Arrow or Up Arrow key. Keep focus inside the tablist.
Severity: Minor
Matches arrow keys to orientation
Reported as: Arrow key navigation failed
Sanity check. Evinced checks that the arrow keys matching the tablist's orientation move focus from one tab to the next. This check runs on several UI patterns.
A tablist's arrow keys should match its layout: the Left Arrow and Right Arrow keys for a horizontal tablist, and the Up Arrow and Down Arrow keys for a vertical one.
When a vertical tablist answers only the Left Arrow and Right Arrow keys, the keys that match what's on screen do nothing, and users conclude the tabs have no keyboard support.
Handle the pair of keys that matches the layout and the aria-orientation value, and leave the other pair to scroll the page.
Manages focus across every item
Reported as: Skipping composite arrow key navigation
Sanity check. Evinced checks that the arrow keys reach every tab in the tablist without skipping any. This check runs on several UI patterns.
Every tab should be able to take focus in turn, not just the first few.
When a roving tabindex covers only some tabs, such as those rendered first, the arrow keys stall partway along the tablist. Users can't tell whether they've reached the last tab, and every tab past the gap is out of reach.
Apply one focus method to every tab, and generate it from the same list that renders the tabs so a tab added later isn't left out.
Unreachable Group
Focus lands on the selected tab when users enter the tablist, so the selected tab can always take keyboard focus. A <button> used as a tab can be disabled, but the selected tab is never disabled or taken out of the tab order.
The following example shows a reachable selected tab and two ways to make it unreachable.
<!-- Reachable: the selected tab holds the tab stop -->
<button type="button" role="tab" aria-selected="true" tabindex="0">Violin</button>
<!-- Unreachable: the selected tab is disabled -->
<button type="button" role="tab" aria-selected="true" tabindex="0" disabled>Violin</button>
<!-- Unreachable: the selected tab is out of the tab order -->
<button type="button" role="tab" aria-selected="true" tabindex="-1">Violin</button>Keeps the selected item reachable
Reported as: Unreachable group
The selected tab must be able to take keyboard focus.
When the selected tab has the disabled attribute, or carries tabindex="-1" while no other tab has tabindex="0", the Tab key passes over the tablist. Keyboard users can't reach any tab, including the one plainly marked as selected.
Set tabindex="0" on the selected tab and move it as the selection moves. When the selected tab's content becomes unavailable, select another tab before disabling it.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Selected Tabs
One tab is selected at a time, and it's the tab whose panel is displayed. Users can select a tab from the keyboard as well as with a pointer. With automatic activation, moving focus to a tab selects it. With manual activation, Enter or Space selects the focused tab. The selected tab also holds the tablist's tab stop, as described in Unreachable Group.
A tablist that displays several panels at once is rare. Content that users open section by section is usually better built as an accordion.
Selects from the keyboard
Reported as: No keyboard selection
A tab must be selectable from the keyboard.
When selection is wired only to a click handler on a generic element, keyboard users can move focus to a tab and can't display its panel. Focus moves and the focus indicator shows, so the failure appears only when they try to choose.
Select the focused tab on Enter and Space, or on focus with automatic activation, by calling the same code a click runs. Update aria-selected and display the panel in the same step.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Acts on Space when focused
Reported as: Spacebar does not trigger selection
Pressing Space on a focused tab must select it, unless the tab was selected automatically when it received focus.
When a tab handles Enter and ignores Space, or lets Space scroll the page, keyboard users get no selection and no error. Most conclude the tab can't be chosen.
Handle Space on the tabs, run the same code a click runs, and call preventDefault so the page doesn't scroll. A <button> with role="tab" fires a click on Space by itself.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Declares multiple selection when supported
Reported as: Aria-multiselectable attribute required
A tablist that lets users select more than one tab must set aria-multiselectable="true".
The attribute defaults to false, so when it's missing, screen reader users are told only one tab can be selected. When they hear several tabs reported as selected, they take it for a fault, or they never learn they can display more than one panel.
Set aria-multiselectable="true" on the tablist and aria-selected="true" on each selected tab, and display the panel of every selected tab at once. ARIA doesn't yet define how a tab reports that its panel is displayed, beyond aria-selected.
Severity: Moderate
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Event Listener Type
When script in a tablist responds to keys, it listens for keydown or keyup. The keypress event is deprecated, and browsers don't fire it for many keys, including the arrow keys, Home, End and Escape.
Avoids the keypress event
Reported as: Deprecated keypress listener
A tablist's key handling should use keydown or keyup, not keypress.
When a handler listens for keypress, keys the browser doesn't send through that event go unanswered, and keyboard users find the tablist ignoring keys it's meant to respond to.
Replace each keypress listener with a keydown listener, and read the key from event.key.
Severity: Minor
FAQ
Which WCAG success criteria do the tablist issues relate to?
Most relate to 4.1.2 Name, Role, Value: the tablist and panel roles, each tab's and panel's name, the selected state, the disabled state, and keeping interactive elements out of tabs.
The tab role, the tablist's name, displaying the right panel and exposing only that one, the set size and positions, aria-controls, a single tab stop, and aria-activedescendant relate to 1.3.1 Info and Relationships.
Reaching the tablist, landing on the selected tab, selecting tabs, and moving between them with the keyboard relate to 2.1.1 Keyboard. Keeping focus in place after a selection relates to 1.3.2 Meaningful Sequence, and keeping hidden panels out of the tab order to 2.4.11 Focus Not Obscured (Minimum).
Distinct tab names, taking the panel's name from its tab, hiding inactive panels from view, and wrapping focus at the ends are among the criteria that map to no WCAG success criterion and are best practices.
Why are some tablist issues marked as Critical and others as Minor?
Severity reflects how much an issue gets in users' way. Critical issues stop users outright: a tablist with no role, unnamed tabs, no reported selection, a panel that isn't displayed or exposed, or tabs keyboard users can't reach, select, or move through.
Serious issues confuse users though tasks are still possible, such as duplicate tab names, a missing tab or panel role, inactive panels left on screen or in the tab order, a wrong disabled state, or tabs that load other pages.
Moderate issues include an unnamed panel, set counts that can't be true, and a tab stop on every tab. Minor issues are mostly an unnamed tablist, attributes set in the wrong place, or values assistive technology can't use.
The aria-owns checks and the checks on where the tablist role sits and on arrow-key focus carry no severity: Evinced reports them as general and sanity checks.
Related Patterns
- Accordion: stacked sections, each under its own heading and button, that can display more than one at once
- Carousel: presents one slide of a set at a time, often with tabs or buttons for choosing the slide
- Site Navigation: links that move to other pages, sometimes styled to look like tabs
- Menu: a menu bar holds commands and can look like a row of tabs