jQuery Accessible Accordion system, using ARIA

Find/fork me on Github

Other accessible plugins

The jQuery plugin will transform a simple list of hx and contents into a fantastic-shiny accordion system, using ARIA.

Here are how-to-use and some examples of this plugin.

Last updates

29th of May, 2019: Fixed several issues with jQuery 3.x (#35, #33). Thanks to everyone who helped to solve these.

15th of August, 2018: Simplified script to be compliant with last version of Design Pattern. Thanks to @goetsu and @zigazou for mentionning this point!

1st of January, 2018: fixed multiple issues when nested accordions, thanks to a fantastic work made by Yvain Liechti.

21st of September, 2017: fixed an issue in the fix added when missing ids on accordion wrapper, reported by @Spone.

It’s robust & accessible

It’s robust

As it relies on a normal HTML structure (a list hx and contents), even if the JavaScript isn’t loaded, your page will work, it’s the miracle of progressive enhancement.

It’s accessible

It is based on ARIA Design Pattern for accordions.

It loves keyboards

Keyboard navigation is supported too, here are the shortcuts:
(Left and Right are inverted on RTL websites)

If you focus on the accordion “buttons”:

  • use Up/Left to put focus on previous accordion button,
  • use Down/Right to put focus on next accordion button
  • use Home to put focus on first accordion button (wherever you are in accordion buttons)
  • use End to put focus on last accordion button (wherever you are in accordion buttons)

And strike space or return on an accordion button to open/close it.

It’s lightweight and free

Lightweight

Its weight is only:

  • 12kb (development, readable by humans);
  • 4.4kb (minified, readable by machines);
  • 1.3kb minified and gzipped (readable by… mutants‽‽)

Free

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”. ;)

How does it work?

Basically it will transform this:

<div class="js-accordion" data-accordion-prefix-classes="your-prefix-class">
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">First tab</h2>
   <p>Content of 1st tab</p>
 </div>
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">Second tab</h2>
   <p>Content of 2nd tab</p>
 </div>
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">Third tab</h2>
   <p>Content of 3rd tab</p>
 </div>
</div>

Into this:

<div class="your-prefix-class" 
   data-accordion-prefix-classes="your-prefix-class"
   role="tablist" aria-multiselectable="true">

 <button id="accordion1_tab1" 
     class="js-accordion__header your-prefix-class__header" 
     aria-controls="accordion1_panel1" aria-expanded="false" 
     role="tab" aria-selected="true">
       First tab
 </button>

 <div id="accordion1_panel1" 
     class="js-accordion__panel your-prefix-class__panel" 
     aria-labelledby="accordion1_tab1" 
     role="tabpanel" aria-hidden="true">

   <h2 class="your-prefix-class__title" tabindex="0">First tab</h2>
   <p>Content of 1st tab</p>

 </div>
 … etc…
</div>

The plugin will do the rest (all ids, ARIA attributes, buttons are generated on the fly).

How to use it

Just download the plugin:

jQuery accessible accordion using ARIA on Github

And use jQuery of course :)

You may also use npm command: npm i jquery-accessible-accordion-aria or bower: bower install jquery-accessible-accordion-aria

Then, follow the conventions given in this minimal example.

<div class="js-accordion" data-accordion-prefix-classes="your-prefix-class">
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">First tab</h2>
   <p>Content of 1st tab</p>
 </div>
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">Second tab</h2>
   <p>Content of 2nd tab</p>
 </div>
 <div class="js-accordion__panel">
   <h2 class="js-accordion__header">Third tab</h2>
   <p>Content of 3rd tab</p>
 </div>
</div>

The minimal style needed is:

.your-prefix-class__panel[aria-hidden=true] {
  display: none;
}

Do not forget to call the plugin:

$(function () {
   $('.js-accordion').accordion();
});

All options of the plugin

var defaultConfig = {
 headersSelector: '.js-accordion__header',
 panelsSelector: '.js-accordion__panel',
 buttonsSelector: 'button.js-accordion__header',
 buttonsGeneratedContent: 'text', // html
 button: $('', {
  class: 'js-accordion__header',
  type: 'button'
 }),
 buttonSuffixId: '_tab',
 multiselectable: true,
 prefixClass: 'accordion',
 headerSuffixClass: '__title',
 buttonSuffixClass: '__header',
 panelSuffixClass: '__panel',
 direction: 'ltr', // rtl
 accordionPrefixId: 'accordion'
};

When calling the plugin, you may set up options as you want. For example:

$(function () {
   $('.js-accordion').accordion({ buttonsGeneratedContent: 'html' });
});

Will call the plugin and generate buttons keeping HTML content in the Hx.

How to style it (nicely)

In this example page, I’ve used data-accordion-prefix-classes="minimalist-accordion", so all the generated classes will start with .minimalist-accordion (.minimalist-accordion__header, .minimalist-accordion__panel and .minimalist-accordion__title).

.minimalist-accordion__panel[aria-hidden=true] {
  display: none;
}

.minimalist-accordion__header {
  display: block;
}

/* title opened */
.minimalist-accordion__header[aria-expanded="true"]:before {
  content: "- ";
}
/* title closed */
.minimalist-accordion__header[aria-expanded="false"]:before {
  content: "+ ";
}

/* title selected */
.minimalist-accordion__header[aria-selected="true"]:after {
  content: " (sel)";
}
/* title non selected */
.minimalist-accordion__header[aria-selected="false"]:after {
  content: " (unselc)";
}

Bonus: wanna see it animated?

You should click on this link to activate them: Activate animations for this accordion.

What will happen? I will change the attribute data-accordion-prefix-classes="minimalist-accordion" to animated-accordion, without changing (almost) anything else to this page. Magic? No. :)

The magic is the same used for my jQuery simple and accessible hide-show system animated.

In fact, it is possible using some CSS transitions. You have to keep in mind several things to keep it accessible:

  • You can’t animate the display property, and height property might be complicated too to animate.
  • So you can’t use display: none; to hide a content (even for assistive technologies).
  • You have to set up visibility to visible or hidden to show/hide a content.
  • Basically, you should animate max-height, opacity (if needed), and use visibility to hide content to assistive technology.

So here is the CSS code (unprefixed):

.animated-accordion__panel {
 display: block;
 overflow: hidden;
 opacity: 1;
 transition: visibility 0s ease, max-height 1s ease, opacity 1s ease ;
 max-height: 100em;
 /* magic number for max-height = enough height */
 visibility: visible;
 transition-delay: 0s;
 margin: 0;
 padding: 0;
}
/* This is the hidden state */
[aria-hidden=true].animated-accordion__panel {
 display: block;
 max-height: 0;
 opacity: 0;
 visibility: hidden;
 transition-delay: 1s, 0s, 0s;
 margin: 0;
 padding: 0;
}

Here is the trick: from “hidden” to “visible” state, visibility is immediately set up to visible, and max-height/opacity are “normally” animated.

From “visible” to “hidden” state, the visibility animation is delayed. So the content will be immediately hidden… at the end of the animation of max-height/opacity.

Bonus: content opened by default, other options

Content opened by default

If you want to have an accordion content opened by default, just add the attribute data-accordion-opened="true" on a js-accordion__panel, example:

<div class="js-accordion__panel" data-accordion-opened="true">
 Second tab
</h2>

And the plugin will open its content.

Multi-selectable or not?

The ARIA Design Pattern for accordions allows to have several accordion panels opened at the same time (which is shown by the attribute aria-multiselectable="true"). However, you might need to avoid this for design purposes or client request. To do this, you may set this attribute on the accordion container: data-accordion-multiselectable="none". Example:


  <div class="js-accordion" data-accordion-multiselectable="none" …>
  

This option will set up aria-multiselectable="false" and the plugin will allow only one panel to be opened at the same time.

Other options

Even if you may set up option in the call of the plugin, you may use some data-* attributes to specify options (for compatibility and historical reasons). data-accordion-prefix-classes will have the same result as option prefixClass. Same for data-accordion-button-generated-content, will set up buttonsGeneratedContent.

Older updates

25th of June, 2017: added option buttonsGeneratedContent, that allows to generate buttons with HTML content or text. Thanks a lot to @oilvier, @kotyy, @chrisgrabinski, @hemoreau and @juliemoynat for reporting it and providing ideas and solutions.

11th of June, 2017: fixed a mistake in the doc about content opened by default, as kindly reported by @Spone and @joosebox.

20th of April, 2017: fixed an issue when missing ids on accordion wrapper, reported by @Spone and @joosebox.

28th of March, 2017: fixed an issue in specific multiselectable option, thanks to swissspidy again.

24th of March, 2017: fixed two issues in keyboard management, thanks to swissspidy.

23th of February, 2017: fixed an issue on multi-selectable option, thanks to tonykim5.

14th of February, 2017: fixed an issue in case of nested accordions, thanks to MEDIUM-ch, and added the version number in JavaScript file, as suggested by @juliemoynat.

1st of February, 2017: added support for UMD, thanks to jules1029.

24th of October, 2016: major refactoring, thanks to the fantastic work made by Yvain Liechti).

  • This is now a real jQuery plugin.
  • Added RTL support for keyboard.
  • Updated base structure (Hx with js-accordion__header must be now in div with js-accordion__panel).
  • Added linters support, etc.

7th of September, 2016: Fixed an issue in case of nested accordions (thanks to John Cheesman).

10th of April 2016: added a section for animating the accordion. You may also install this plugin using bower: bower install jquery-accessible-accordion-aria

20th of January 2016: this plugin is available on NPMjs.com

10th of July, 2015: Added option data-accordion-multiselectable="none", to avoid having several panels opened at the same time.

24th of April, 2015: Fixed a focus issue on iOS 7/8 (thanks to @goetsu). Fixed a “vocal” issue on example (thanks to @cahnory and @ffoodd_fr).