Tabs
Tabs, which are very similar to accordions, are also common web design structures used to organize and hide content, so as not to overwhelm the user. Tabs also additionally require JavaScript to show/hide things based on a user click or focus event.
The key to making tabs accessible is to toggle CSS display properties and some ARIA states on user click or focus events (e.g. toggle aria-selected
, tabindex
, and display:none/block;
and/or aria-hidden='true/false'
).
Best Practices
Tabbed content usually consist of two main parts. First, they consist of the tabs themselves that control the visibility of some area of content below. Second is the content controlled by the tabs. In general, we refer to the former as tabs, and the latter as tab panels.
Tab Controls
Each tab control is set up as a link inside an unordered list structure. The unordered list should have an aria
role='tablist'
. Using links rather than buttons for tab controls seems to be common practice, though we could see an argument made for making tab controls buttons instead.All the tab controls have an aria
role='tab'
.Each tab control has a unique
id
andhref
which is associated with a tab panel’sid
.Each tab link also has a unique
aria-controls
attribute which references its tab panel’sid
({name-of-fruit}
).Every tab control has an
aria-selected
attribute that is set to eithertrue
orfalse
. Iftrue
, the tab control has been clicked or focused on, and the tab panel associated with it should be visible. Iffalse
, the tab control is not selected, and its corresponding tab panel is hidden.Also, for the tab controls that are not currently selected, we place
tabindex='-1'
on them to take them out of the tab order. If there are 10-12 tab controls on your web page, user’s of assistive technology will now not have to tab through all of them to reach the content in the tab panel.
Tab Panels
A CSS class of
.hidden
or.current
is toggled on each tab panel when the active tab control changes focus, so that when a panel should be hidden,display: none;
is applied to the panel, and when it should be visible,display: block;
is applied instead. When a panel hasdisplay: none;
applied, the tab panel is neither visible nor perceivable by screen reader, and assistive technologies will skip over this content.All the tab panels have an aria
role='tabpanel'
.Each tab panel also has an
aria-labelledby
attribute which serves a similar purpose to how thearia-controls
acted in our accordions example.- The
aria-labelledby
attribute is set to theid
of the corresponding tab control, such as:aria-labelledby='tab-{#}'
to give users of assistive technology a more comprehensive idea of how the tabs and tab panels are linked.
- The
For details on what we’ve done with JavaScript to make the tabs work, see the comments/annotations we have made within the JS code snippet itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <div class="tabbed-content"> <ul class="tabs-list" role='tablist'> <li class="current"> <a aria-controls="apples" aria-selected="true" href="#apples" id="tab-apples" role='tab' class='tab selected'> Apples </a> </li> <li> <a aria-controls="bananas" href="#bananas" aria-selected='false' id="tab-bananas" role='tab' tabindex='-1' class='tab'> Bananas </a> </li> <li class='last'> <a aria-controls='pears' aria-selected="false" href="#pears" id="tab-pears" role='tab' tabindex='-1' class='tab'> Pears </a> </li> </ul> <div aria-labelledby="tab-apples" class="tab-panel current" id="apples" role='tabpanel'> <h2>Apples</h2> <p> Lorem ipsum dolor sit amet, urna class vestibulum tincidunt atque, habitasse sit wisi erat dapibus. Vitae curae natoque a, donec nulla conubia in mollis. Sapien pede in tortor, lectus neque vitae in et, vitae aliquam eget orci at, non turpis faucibus id morbi. Elit tempor turpis donec inceptos, fringilla arcu sollicitudin ligula magna, sed justo viverra lacus erat, vestibulum id in justo nulla. Viverra dui leo donec, aptent deserunt nostra magnis lobortis, id ultrices ac aenean, interdum vestibulum rhoncus phasellus libero.</p> </div> <div aria-labelledby="tab-bananas" class="tab-panel hidden" role='tabpanel' id="bananas"> <h2>Bananas</h2> <p>Lorem ipsum dolor sit amet, urna class vestibulum tincidunt atque, habitasse sit wisi erat dapibus. Vitae curae natoque a, donec nulla conubia in mollis. Sapien pede in tortor, lectus neque vitae in et, vitae aliquam eget orci at, non turpis faucibus id morbi. Elit tempor turpis donec inceptos, fringilla arcu sollicitudin ligula magna, sed justo viverra lacus erat, vestibulum id in justo nulla. Viverra dui leo donec, aptent deserunt nostra magnis lobortis, id ultrices ac aenean, interdum vestibulum rhoncus phasellus libero.</p> </div> <div aria-labelledby="tab-pears" class="tab-panel hidden" id="pears" role='tabpanel'> <h2>Pears</h2> <p>Lorem ipsum dolor sit amet, urna class vestibulum tincidunt atque, habitasse sit wisi erat dapibus. Vitae curae natoque a, donec nulla conubia in mollis. Sapien pede in tortor, lectus neque vitae in et, vitae aliquam eget orci at, non turpis faucibus id morbi. Elit tempor turpis donec inceptos, fringilla arcu sollicitudin ligula magna, sed justo viverra lacus erat, vestibulum id in justo nulla. Viverra dui leo donec, aptent deserunt nostra magnis lobortis, id ultrices ac aenean, interdum vestibulum rhoncus phasellus libero.</p> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | $(function(){ var index = 0; var $tabs = $('a.tab'); $tabs.bind( { // on keydown, // determine which tab to select keydown: function(ev){ var LEFT_ARROW = 37; var UP_ARROW = 38; var RIGHT_ARROW = 39; var DOWN_ARROW = 40; var k = ev.which || ev.keyCode; // if the key pressed was an arrow key if (k >= LEFT_ARROW && k <= DOWN_ARROW){ // move left one tab for left and up arrows if (k == LEFT_ARROW || k == UP_ARROW){ if (index > 0) { index--; } // unless you are on the first tab, // in which case select the last tab. else { index = $tabs.length - 1; } } // move right one tab for right and down arrows else if (k == RIGHT_ARROW || k == DOWN_ARROW){ if (index < ($tabs.length - 1)){ index++; } // unless you're at the last tab, // in which case select the first one else { index = 0; } } // trigger a click event on the tab to move to $($tabs.get(index)).click(); ev.preventDefault(); } }, // just make the clicked tab the selected one click: function(ev){ index = $.inArray(this, $tabs.get()); setFocus(); ev.preventDefault(); } }); var setFocus = function(){ // undo tab control selected state, // and make them not selectable with the tab key // (all tabs) $tabs.attr( { tabindex: '-1', 'aria-selected': 'false' }).removeClass('selected'); // hide all tab panels. $('.tab-panel').removeClass('current'); // make the selected tab the selected one, shift focus to it $($tabs.get(index)).attr( { tabindex: '0', 'aria-selected': 'true' }).addClass('selected').focus(); // handle parent <li> current class (for coloring the tabs) $($tabs.get(index)).parent().siblings().removeClass('current'); $($tabs.get(index)).parent().addClass('current'); // add a current class also to the tab panel // controlled by the clicked tab $($($tabs.get(index)).attr('href')).addClass('current'); }; }); |
Live Example
Apples
Lorem ipsum dolor sit amet, urna class vestibulum tincidunt atque, habitasse sit wisi erat dapibus. Vitae curae natoque a, donec nulla conubia in mollis. Sapien pede in tortor, lectus neque vitae in et, vitae aliquam eget orci at, non turpis faucibus id morbi. Elit tempor turpis donec inceptos, fringilla arcu sollicitudin ligula magna, sed justo viverra lacus erat, vestibulum id in justo nulla. Viverra dui leo donec, aptent deserunt nostra magnis lobortis, id ultrices ac aenean, interdum vestibulum rhoncus phasellus libero.