<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>WCAG ARIA accessible tab navigation</title>
<link rel='stylesheet prefetch' href='css/kxqoaa.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1><a href="http://websemantics.uk/articles/accessible-tab-navigation/">Accessible tab navigation to meet <abbr title="Web Content Accessibility Guidelines">WCAG</abbr> 2 using <abbr title="Accessible Rich Internet Applications">ARIA</abbr> roles</a></h1>
<p>Use the <kbd>Tab</kbd> key to jump link to visible link, and the arrow keys to navigate across the tabs.</p>
<p>A <strong>white focus outline</strong> is applied as an aid to visualisation but it is <strong>not</strong> a requirement.</p>
<div class=tl_container>
<!-- A plain set of anchor links in HTML -->
<ul class="tl_list">
<li><a href="#A" class=a>Section A</a></li>
<li><a href="#B" class="b ON">Section B</a></li>
<li><a href="#C" class=c>Section C</a></li>
</ul>
<section id=A class="tl_section a">
<h2>Content heading A</h2>
<p>Copy with an <a href="#">Example focusable link</a> as content for A1.</p>
</section>
<section id=B class="tl_section b">
<h2>Content heading B</h2>
<p>Copy with an <a href="#">Example focusable link</a> as content for B1.</p>
</section>
<section id=C class="tl_section c">
<h2>Content heading C</h2>
<p>Copy without a focusable link as content for C1.</p>
<!-- A second embedded tablist example -->
<ul class="tl_list">
<li><a href=#D class=d>Sub-section D</a></li>
<li><a href=#E class="tl_lnk-on e">Sub-section E</a></li>
</ul>
<div id=D class="tl_section d">
<h2>Content heading D</h2>
<p>Copy without a focusable link as content for D.</p>
</div>
<div id=E class="tl_section e">
<h2>Content heading E</h2>
<p>Copy without a focusable link as content for E.</p>
</div>
</section>
</div>
<p>Updated (19-03-2017) to include up / down arrow keys and nested tablists.</p>
<p>Available as a Github repo: <a href="https://github.com/2kool2/accessible-tabs" target=_blank title="[new window]">Accessible tabs</a>.</p>
<p>There's an option to allow mouse hover to activate a tab by adding the class "hoverable" to the tablist <code>ul</code>. Not so sure it's a good idea though.</p>
<!-- <p>As used on the <a href="http://www.tesco.com/easter/" target=_blank title="[new window]">Tesco Easter 2016 hub</a> (Opening hours results). <a href="http://websemantics.uk/tesco/easter.2016/" target=_blank title="[new window]">Permanent copy</a></p> -->
<p id=addToggle></p>
<h2>Alternatively</h2>
<p>The method provided here is a furtherence of Heydon Pickering's <a href="http://heydonworks.com/practical_aria_examples/#tab-interface">Tab interface</a>.</p>
<p>Please read and consider Thierry Koblentz's article: <a href="http://cssmojo.com/tab-panel-the-right-way/?mc_cid=29c24aed5b&mc_eid=278af32c16#visual-clutter-started-it-all">Tab Panel, the right way…</a> which doesn't use anchors as tab activators. Pankaj Parashar has a supporting CodePen <a href="https://codepen.io/pankajparashar/pen/oJEAF">Awesome Accessible Tabpanel</a>.</p>
<p>Jeff Smith's article: <a href="https://simplyaccessible.com/article/danger-aria-tabs/">Danger! ARIA tabs</a> has good arguments, though I'm not completely sold on the suggested solution: <a href="https://codepen.io/jeffsmith/pen/mPByya/">Standard tabs without ARIA roles </a>.</p>
<p class=smaller><a target=_blank title="[new window]" href="https://codepen.io/2kool2/pens/public/?grid_type=list#">Pens by Mike Foskett</a> — <a target=_blank title="[new window]" href="https://websemantics.uk/">webSemantics</a></p>
<script src='https://codepen.io/2kool2/pen/kXQoAA?'></script>
<script src="js/index.js"></script>
</body>
</html>
/*Downloaded from https://www.codeseek.co/2kool2/wcag-aria-accessible-tab-navigation-Kzaddm */
/* Tab styling (optional) */
.tl_container {margin: 2rem 0;}
.tl_list {
display: flex;
padding: 0;
margin: 0;
align-items: center;
justify-content: left;
list-style: none;
}
.tl_list li {
margin: 0;
width: 33.3333%;
text-align: center;
border-radius: 4px 4px 0 0;
}
.tl_list a {
color: #fff;
padding: 1rem 0.5rem;
display: block;
border: none;
text-decoration: none;
text-shadow: none;
}
.tl_list a:hover,
.tl_list a:focus {
text-shadow: 0 2px 4px #000
}
/* White focus ring - so you can see when it's focussed (not required) */
.tl_section h2:focus {
outline: 1px solid currentcolor;
}
/* Section styling (optional) */
.tl_section {
padding: 1rem 1.5rem;
min-height: 8rem;
border-radius: 0 0 4px 4px;
text-shadow: none;
}
.tl_section h2 {
margin-top: 1rem;
}
/* tab & panel colour */
.a,
.a:hover,
.a:focus {background-color: rgba(255,85,160,.3);}
.b,
.b:hover,
.b:focus {background-color: rgba(160,255,85,.3);}
.c,
.c:hover,
.c:focus {background-color: rgba(85,160,255,.3);}
.d,
.d:hover,
.d:focus {background-color: rgba(85,160,255,.25);}
.e,
.e:hover,
.e:focus {background-color: rgba(85,160,255,.5);}
/*
Section styling (required)
Removes hidden sections from the keychain.
Only used when JavaScript is available.
*/
[role="tabpanel"][aria-hidden="true"] {
display: none;
}
/*Downloaded from https://www.codeseek.co/2kool2/wcag-aria-accessible-tab-navigation-Kzaddm */
/*
Simple Accessible Tab control v6.1.1 19-03-2017
Author: Mike Foskett
Incept: 09-03-2016
Article: http://websemantics.uk/articles/accessible-tab-navigation/
Furtherence of: Heydon Pickering - http://heydonworks.com/practical_aria_examples/#tab-interface
Uses ARIA attributes (via CSS) to control what's visible.
Requires (tested):
addEventListener (IE8+)
preventDefault (IE9+)
classList (IE10+)
querySelectorAll (IE9+)
Required parameter:
none
Optional parameters:
tabListClass : "tl_list" (default)
onClass : "ON" (default)
Customisable, set default tab on.
Used for initialisation only.
hoverableClass : "tl-hoverable" (default)
Customisable, allow mouse-hover to switch tabs.
Apply to the navigation ul.
Version 6.1.1 - 19-03-2017 - Added: up/down arrow keys; nested tablists
Version 6.1 - 16-08-2016
Version 6 - 10-07-2016
Version 5.2 - 17-03-2016
Version 5 - 09-03-2016
Version 4 - 27-01-2012
Version 3 - 25-01-2012
Version 1 - 27-05-2010
*/
var accessibleTabs6 = (function () {
"use strict";
var d = document;
var tabListClass;
var onClass;
var hoverableClass;
var setTabsOff = function (tabList) {
var i = tabList.tabs.length;
while (i--) {
setTab(tabList.tabs[i], false);
}
};
var setTab = function (tab, switchOn) {
if (tab && tab.panelId) {
d.getElementById(tab.panelId).setAttribute("aria-hidden", !switchOn);
tab.setAttribute("aria-selected", switchOn);
tab.setAttribute("tabindex", switchOn ? "0" : "-1");
}
};
var _activateTab = function (e) {
var tab = e.target;
if (e.preventDefault) {
e.preventDefault();
setTabsOff(tab.tabList);
setTab(tab, true);
d.getElementById(tab.panelId).children[0].focus();
}
};
var _keypressed = function (e) {
var tab = e.target;
var newNo = -1;
var tabs = tab.tabList.tabs;
var maxNo = tabs.length - 1;
if (e.keyCode === 37 || e.keyCode === 38) { // left / up arrow
newNo = (tab.no === 0) ? maxNo : tab.no - 1;
}
if (e.keyCode === 39 || e.keyCode === 40) { // right arrow / down arrow
newNo = (tab.no === maxNo) ? 0 : tab.no + 1;
}
if (newNo > -1) {
setTabsOff(tab.tabList);
setTab(tabs[newNo], true);
tabs[newNo].focus();
}
};
var _tabHovered = function (e) {
var a = e.target;
var useHover = a.tabList.classList.contains(hoverableClass);
if (useHover) {
_activateTab(e);
}
};
var _events = function (tab) {
tab.addEventListener("click", _activateTab, false);
tab.addEventListener("keydown", _keypressed, false);
tab.addEventListener("mouseover", _tabHovered, false);
};
var _initialiseAriaAttributes = function (tab) {
var tabPanel = d.getElementById(tab.panelId);
tab.parentNode.setAttribute("role", "presentation");
tab.setAttribute("role", "tab");
tab.setAttribute("aria-controls", tab.panelId);
tabPanel.setAttribute("role", "tabpanel");
tabPanel.setAttribute("aria-labelledby", tab.id);
// Make first <section> object keyboard focussable
// Preferably a heading
// Tabindex=0 to work both forwards and backwards through the keychain
tabPanel.children[0].setAttribute("tabindex", "0");
};
var _setUpConfig = function (cfg) {
tabListClass = cfg.tabListClass || "tl_list";
onClass = cfg.onClass || "ON";
hoverableClass = cfg.hoverableClass || "tl-hoverable";
};
var _setUpTab = function (tab, panelId, count) {
tab.no = count;
tab.id = "tab-" + panelId;
tab.panelId = panelId;
_initialiseAriaAttributes(tab);
_events(tab);
};
var _initialiseTabList = function (tabList) {
var defaultTab = 0;
var panelId;
var tabPanel;
var i;
if (tabList) {
tabList.setAttribute("role", "tablist");
tabList.tabs = tabList.getElementsByTagName("a");
i = tabList.tabs.length;
while (i--) {
tabList.tabs[i].tabList = tabList;
panelId = tabList.tabs[i].href.slice(tabList.tabs[i].href.lastIndexOf("#") + 1);
tabPanel = d.getElementById(panelId);
if (tabPanel) {
_setUpTab(tabList.tabs[i], panelId, i);
if (tabList.tabs[i].classList.contains(onClass)) {
defaultTab = i;
// onClass only used to declare intial state, so remove from DOM
tabList.tabs[i].classList.remove(onClass);
d.getElementById(panelId).classList.remove(onClass);
}
}
}
setTabsOff(tabList);
if (tabList.tabs[defaultTab] && tabList.tabs[defaultTab].panelId) {
setTab(tabList.tabs[defaultTab], true);
}
}
};
var _isMustardCut = function (e) {
// check browser feature support (IE10+)
return (
(typeof d.querySelectorAll === "function") &&
d.addEventListener &&
!!d.documentElement.classList // IE10+
);
};
var init = function (cfg) {
var tabLists;
var i;
if (_isMustardCut()) {
_setUpConfig(cfg);
tabLists = d.getElementsByClassName(tabListClass);
i = tabLists.length;
while (i--) {
_initialiseTabList(tabLists[i]);
}
}
};
return {
init: init
};
}());
accessibleTabs6.init({
tabListClass : "tl_list", // default, may omit
onClass : "ON", // default, may omit
hoverableClass : "tl-hoverable" // default, may omit
});