Tabs
Hide and disclose panels of content via a series of interactive tabs.
Last updated
Was this helpful?
Hide and disclose panels of content via a series of interactive tabs.
Last updated
Was this helpful?
A tab is a control that allows the user to select and display a single panel of content from a group of choices. By decluttering the user-interface in this way, we say that tabs follow the principal of progressive disclosure.
tabs: the composite patterns as a whole, containing a tablist, tabs and tabpanels
tabs heading: the heading that immediately precedes the tabs widget
tab list: contains two or more tabs
tab: a type of button that displays it's associated tabpanel
selected tab: the currently selected tab
tab panel: contains the content related to the tab
tab heading: the offscreen heading that maintains correct heading structure
autoSelect: for keyboard users, tab selection can either follow keyboard focus (known as auto selection), or require an additional ENTER
or SPACEBAR
press to set selection (known as manual selection).
Tab list must be preceded by a heading. All tabs must be thematically related to this heading. For example, a set of 'Shipping Services' tabs might contain a tab each for USPS, FedEx and UPS.
Tab list must have exactly one selected tab.
If all tab panel content is rendered on page load, tabs should be configured with autoSelect
enabled.
If all tab panel content is rendered lazily on client (i.e. using AJAX call), tabs should be configured with autoSelect
turned off.
This section provides guidance for keyboard, screen reader and pointing devices.
For tabs with autoSelect
enabled, ARROW
keys move keyboard focus to next/previous tab and also select that tab (i.e. aria-selected="true"
).
For tabs without autoSelect
enabled, ARROW
keys move keyboard focus to next/previous tab, but ENTER
or SPACEBAR
key is required to set the tab to a selected state.
If tab panel contains focusable element(s), TAB
key on selected tab must move focus to first focusable element in tab panel.
If tab panel does not contain focusable element(s), TAB
key on selected tab must move focus to next focusable element on page.
Tab must be announce as "Tab".
Tab label must be announced, for example "Select Shipping for me".
Tab selected state must be announced.
Virtual cursor navigation can move from tab to tab without changing the active tab selection.
Our first example implementation will create a tabs widget with autoSelect
configuration enabled. All of the tab panel content will be rendered to the DOM on server side load.
The three layers are:
Content (HTML)
Presentation (CSS)
Behaviour (JS)
The tabs and their related content elements can be fully visible and accessible without CSS and JavaScript as simple hyperlinks and page anchors respectively.
For a tabs widget where content is not rendered on first server side load, using this progressive enhancement type approach is not as applicable.
The goal of our content layer is to add all of our tabs and their respective panel content to the page.
For the purposes of this example, all panel content will be rendered server-side. You may wish to consider lazy-loading the content of each panel with AJAX. If you do utilise lazy-loading, be aware that your content will not be available in a non-JavaScript scenario.
Links
The tabs begin life as simple same-page navigation links, linking to the content anchors (panels) below it on the same page:
This structure has been chosen carefully. It allows us to display tabs horizontally and vertically simply by changing the second class (to tabs--horizontal or tabs--vertical).
NOTE: we have found that in some browsers, activating a same page link will only scroll the browser to the target, but the focus is left behind on the link. Adding tabindex="-1" also helps move and set focus on the target element.
Checkpoint
That's it! Our content is available and accessible to anyone in a non-CSS and non-JS state.
The goal of our presentation layer is to style the links to look like folder style tabs.
How you choose to style the links is outside the scope of this document, because every website likes to make their tabs look slightly different!
Flash of Unstyled Content (FOUC)
We have chosen an arbitrary value of 150px for our example. After our JavaScript initialises the widget, it's height will grow or shrink to match the content of the currently selected panel. Of course if fixed height is what you desire, then you can leave the fixed value in place.
Checkpoint
Our tabs now appear visually like tabs, and the panel content is still fully operable without JavaScript (albeit with ugly vertical scrollbars).
The goal of our JavaScript is to implement our interaction design.
Plugin Boilerplate
We start by caching references to our most important elements:
ARIA Roles
How does a screen reader know this is a tabs widget? We must add ARIA roles to the tab list, tabs, and panels.
Remove Link Behaviour
We currently have links nested inside of our tab elements. To avoid conflicts with our tabs we must remove any semantics and behaviour, effectively turning them into span
tags.
ARIA States
How does a screen reader know which tab is currently selected and which panel is visible? We must add aria-selected
and hidden
states.
ARIA Properties
How does a screen reader know which panel belongs to which tab, and the label of each panel? We must add aria-controls
and aria-labelledby
properties.
Roving Tabindex
If there are many tabs it would require many TAB
key presses to navigate past the widget, therefore tabs should be navigated with ARROW
keys instead.
Only one tab can be focussable at any given time. This is always the "selected" tab. When a user tabs away from the widget and then back again, focus will return to this "selected" tab.
State Management
When the roving tabindex changes, we must update the aria-selected
and hidden
states.
Prevent Page Scroll
Widget Init
Finally we can mark our widget as initialised. Now our CSS rules for our progressively enhanced widget will kick in.
Final Checkpoint
We have enhanced our markup with ARIA roles, states and properties for screen reader users, and implemented keyboard behaviour.
We have some experimental JavaScript modules that may assist you with creation of an accessible tabs widget:
Selecting a tab should update the visible panel without a full page reload. If a full page load is required instead (i.e. acting like a link), please see the section below for more details.
Experience the tab pattern in action on our .
Examine the required markup structure by viewing our .
View a fully-style example on our site.
To maintain correct heading structure, tab panels should contain an heading. The level of this panel heading must be exactly one level lower than the heading preceding the tablist. The heading text must match the corresponding tab text.
Only one tab can be keyboard focusable at any time. This is known as a .
The sample follows the strategy; we build in a layered fashion that allows everyone to access the basic content and functionality of a web page.
We call this markup structure our ; our CSS and JavaScript will be expecting this exact DOM structure convention.
may occur before JavaScript initialises the widget, i.e. all panel content may be visible briefly. One way to alleviate this is to set a fixed height on the tab panel container:
Our selectors are based on our .
This behaviour is known as a . We provide a sample module for you to reference.
When the selected tab has focus, we must prevent arrow keys and spacebar from scrolling the page. We provide another module, , to make this trivial.
- Useful for implementing the arrow key behaviour to change tabs
- Useful for preventing keys from scrolling page while focus is on a widget