jQuery Accessible tab panel system, using ARIA
The jQuery plugin will transform a simple list of anchors to contents into a fantastic-shiny tabpanel system, using ARIA.
Here are how-to-use and some examples of this plugin.
- It’s robust & accessible
- It loves keyboard
- It’s light and free
- It’s (highly) customisable
- It’s been used on
Robust: as it relies on a normal HTML structure (a list of links with internal anchors), even if the JavaScript isn’t loaded, your page will work, it’s the miracle of progressive enhancement.
Accessible: it is based on ARIA Design Pattern for tabpanel.
Its weight is only:
- 13kb (development, readable by humans);
- 4kb (minified, readable by machines);
- 1kb minified and gzipped (readable by… mutants‽‽)
It takes more time to read the documentation than to install and use the plugin.
No license problem: it uses MIT license, so it’s free, open-source and you can do whatever you want with it, including commercial use. This permission notice shall be included in all copies or substantial portions of it.
However, it is not prohibited to tell me that you’ve used it, or send me a little “thank you”. ;)
The classes set by default do not add any CSS styles. The only one needed is:
.js-tabcontent[aria-hidden=true] { display: none; }
just to hide unactive tabs. So basically, you can do whatever you want, including for responsive.
Last updates
21st of September 2017: added an option to disable fragments (see example below), as kindly requested by @dennisfrank .
14th of April 2017: added data-selected="1"
attribute to open a tab by default (read the example).
9th of April 2017: linted and reindented code properly.
31st of March 2017: created a special version of the script for nested tabs thanks to @dameert, dedicated file is on the repositery: jquery-accessible-nested-tabs.js (with stricter selectors).
15th of February 2017: fixed an issue to avoid WordPress jQuery conflict mode thanks to @floriantiar, and added the version number in JavaScript file, as suggested by @juliemoynat.
You can see older updates if needed.
Several demos and instructions
How to use it
Just download the plugin:
jQuery accessible tabs using ARIA on Github
And use jQuery of course :)
You may also use npm command: npm i jquery-accessible-tabpanel-aria
.
You may also use bower: bower install jquery-accessible-tabs-aria
.
Then, follow the conventions given in this minimal example (in bold).
- use classes needed (
js-tabs
,js-tablist
,js-tablist__item
,js-tablist__link
,js-tabcontent
) - be careful to use the convention in the links
a href="#id_first" id="label_id_first"
- check that your anchors are working.
- for accessibility purposes (for VoiceOver), the plugin has to give focus to
hx
(h2
,h3
,h4
, etc.) in tab contents.
For Hx
, you have two cases:
- There aren't any in your
js-tabcontent
’s, so specify indata-hx
attribute (will be added withclass="invisible"
, which means visually hidden); - There are some, just tell it to the plugin using
data-existing-hx
attribute.
Example without hx
:
<div class="js-tabs">
<ul class="js-tablist" data-hx="h2">
<li class="js-tablist__item">
<a href="#id_first" id="label_id_first" class="js-tablist__link">1st tab</a>
</li>
<li class="js-tablist__item">
<a href="#id_second" id="label_id_second" class="js-tablist__link">2nd tab</a>
</li>
</ul>
<div id="id_first" class="js-tabcontent">
here the content of 1st tab
</div>
<div id="id_second" class="js-tabcontent">
here the content of 2nd tab
</div>
</div>
Example with hx
:
<div class="js-tabs">
<ul class="js-tablist" data-existing-hx="h2">
<li class="js-tablist__item">
<a href="#id_first" id="label_id_first" class="js-tablist__link">1st tab</a>
</li>
<li class="js-tablist__item">
<a href="#id_second" id="label_id_second" class="js-tablist__link">2nd tab</a>
</li>
</ul>
<div id="id_first" class="js-tabcontent">
<h2>title</h2>
here the content of 1st tab
</div>
<div id="id_second" class="js-tabcontent">
<h2>other title</h2>
here the content of 2nd tab
</div>
</div>
Then call jQuery and the plugin in your page. Yes, that’s all.
How to style it (nicely)
Once you set up the code with your content, style it before activating JavaScript: so it will be nice even if there is no JavaScript.
For example, just imagine you are on poor mobile connexion, and the JavaScript hasn’t (yet) loaded. Or it can be disabled.
You should add classes to the source, and use them this way:
[role="tablist"].my-style {…}
Then activate JavaScript.
Basically, you should rely on ARIA attributes, so styles will be applied only if JavaScript is loaded and well-executed.
Usually, I minify my CSS, but for this example, I didn’t.
Go into the CSS and have a look (search “styles for tabs, examples”), and also in the breakpoints.
I advise you to set it up like this, for example for “How to use it”:
/*
* Styles tabs 1/2/3
*/
/* Styles without JS */
.puce-tab__text {
font-weight: bold;
}
.puce-tab__number {
display: inline-block;
font-size: 1.5em;
width: 1.5em;
height: 1.5em;
background: #882525;
color: #fff;
border-radius: 50%;
font-weight: normal;
}
/* Styles with JS */
[role="tab"].puce-tab {
opacity: .6;
-webkit-transition: all .5s ease;
-moz-transition: all .5s ease;
-o-transition: all .5s ease;
transition: all .5s ease;
}
[aria-selected="true"].puce-tab {
opacity: 1;
font-size: 1.7em;
width: 1.7em;
height: 1.7em;
}
If you need the class invisible
, here is an example:
.invisible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
The plugin has another feature: if you don’t like styling on role attributes, it can generate classes for you only for styling tabs when they are activated.
Look at the last tabs of this page, here is the source:
<ul … data-tabs-prefix-class="last-tabs">
The data-tabs-prefix-class
will add classes on each elements:
<ul … class="… last-tabs-tabs__list">
<li … class="… last-tabs-tabs__item">
<a … class="… last-tabs-tabs__link">…
So with data-tabs-prefix-class="last-tabs"
you have:
last-tabs-tabs__list
for styling theul
;last-tabs-tabs__item
for styling theli
;last-tabs-tabs__link
for styling thea
.
These classes could be used for styling tabs when they have been ARIA-ised (when JavaScript is loaded, if you prefer).
Bonuses: “fragment” management and “in-page link to tab”
As you may notice (or not), now the plugin adds a fragment in the URL when you click or select a tab using the keyboard. It is cool when you want to copy/paste the link of the page you are reading, and the opened tab will be the good one!
If you need to make a link on your page to a id
that is on a tab panel, the plugin will detect it and display the good tab, only for you.
If you want to test, open a new window, and copy/paste this:
https://a11y.nicolas-hoffmann.net/tabs/#light_free
If you need to make a link on your page to a id
that is in a tab panel, the plugin will detect it and display the good tab, only for you.
If you want to test, open a new window, and copy/paste this:
https://a11y.nicolas-hoffmann.net/tabs/#license-cool-anchor
If you need to create in your current page a link to a tab (inside or outside), it is possible:
- Create the link to the content you want to target:
<a href="#link-to-tab">link to tab option</a>
; - Add the class
class="js-link-to-tab"
to it; - And that’s all. The plugin will make it work.
Here is an example: link to tab “Anchor added”
<a href="#anchor-added" class="js-link-to-tab">link to tab “Anchor added”</a>
Bonus: tab panel opened by default
This tab is available but sad, as the second is opened by default, using data-selected="1"
.
If you need a tab to be opened by default, it is possible, using data-selected="1"
on the js-tablist__link
you need to open.
Other tabs are still available, here are the rules for this feature:
- The fragment detection has always priority on this feature;
- If there are several
data-selected="1"
put on tabs (which does not make sense and should never happen), the first one will be used; - If the
data-selected="1"
attribute is set on a disabled tab, of course it won’t be selected.
This tab is also available.
Other bonuses: “disabled” tab management, and disabled fragments
- A sad disabled tab
- How to disable a tab
- How to style them
- Another disabled tab
- How to disable fragments
No content
To disable a tab, you only have to put aria-disabled="true"
on the a
tag. Example:
<li class="js-tablist__item tabs__standard__li">
<a href="#disabled2" id="label_disabled2" class="js-tablist__link tabs__standard__a" aria-disabled="true">Another disabled tab</a>
</li>
The plugin will detect it, and adapt everything:
- The keyboard management (test on this set of tabs);
- The selection of first “non-disabled” tab;
- Etc.
Here is a simple piece of styling:
[aria-disabled="true"],
[aria-disabled="true"]:hover {
background-color: #ddd;
color: #666;
pointer-events: none;
/* for old IEs or browser that don’t support pointer-events */
cursor: not-allowed;
}
No content, I said.
If you need to disable fragments that are automatically added to the URL, it is possible.
You just need to add data-tabs-disable-fragment="1"
to the div class="js-tabs">
.
<div class="js-tabs" data-tabs-disable-fragment="1">
And that’s all. :)
Older updates
17th of November 2016: added a “disabled tab” feature (see example below), on an idea submitted by @gdurelle.
17th of April 2016: fixed two bugs in case of nested tabs.
6th of April 2016: added data-tabs-generated-hx-class
optionnal attribute. This option is useful to specify the class that will be applied to the hx
generated if you use the attribute data-hx
. If this attribute is not specified, the class invisible
will be used.
21st of March 2016: this plugin is available on bower: bower install jquery-accessible-tabs-aria
11th of January 2016: this plugin is available on NPMjs.com
24th of August 2015: complete refactoring:
- code performance;
- adds fragment management;
- use of
js-*
classes (better convention); data-tabs-prefix-class
option;- and link to tab option.