Keyboard Accessible Accordion Plugin

Webfactory GmbH January 26, 2023


Live Preview Download

The wfAccordion plugin makes it easy to help developers create responsive and accessible accordions for sectioned content or FAQs.

wfAccordion JavaScript Library Implementation

How wfAccordion Works
  • Enhance triggers and panels with ARIA attributes: aria-expanded, aria-hidden, aria-disabled.
  • Create unique IDs for use in ARIA relationships: aria-controls, aria-labelledby.
  • Create ARIA relationships between headers and panels
  • Update ARIA states on click/tap
  • Follows the best practices laid out in WAI-ARIA Authoring Practices 1.2 for Accordions
  • Enhance a given placeholder element with a button HTML tag for better keyboard support.
    • Open/close accordion panels using up / down arrows as well as Ctrl + Page Up / Page Down keys
    • Focus the first accordion using End key
    • Focus the last accordion using Home key
  • Compatible with both jQuery and Vanilla JavaScript.

Download and import the wfAccordion

// ES Module
import 'webfactory-accordion/dist/wf.accordion.min';

Build the HTML structure for the accordion

This is an accordion

Accordions are a common design pattern in web design. They are often used to hide large chunks of content from the user intially. The aim is to provide a general overview of the content to the user, so that he can decide which part of it is interesting to him and can expand the content via interaction.

This accordion is expanded initially

Add "data-wf-accordion-expanded" to the accordion's root element to expand its panel initially.

This accordion has been disabled by adding "data-wf-accordion-disabled" to its root element

Add "data-wf-accordion-disabled" to the accordion's root element to disable it.

This accordion's trigger element has nested elements (e.g. a strong or span)

You can use nested elements inside the element with .js-accordion__trigger.

This accordion's trigger element has a nested heading that will become the parent element of the button.

To result in valid HTML, the final DOM needs to avoid nesting block-level elements in buttons.

Initialize the accordion

  // jQuery Only

Apply your own CSS styles to the accordion

* Accordion example styles

.wf-accordion-group {
  margin-top: 15px;

/* All elements succeeding an accordion group use margin-top to create white space */
.wf-accordion-group+* {
  margin-top: 30px;

/* All accordions have borders… */
.wf-accordion {
  border-top: 1px solid #929292;
  border-bottom: 1px solid #929292;

/* …unless they directly succeed another accordion, in which case we reset the top-border
to avoid duplicate white space */ {
  border-top-width: 0;

.wf-accordion__header {
  color: #616161;

/* Please note: The trigger element is a button create via JS. To achieve consistent aesthetics,
 the native button styles have to be resetted here */
.wf-accordion__trigger {
  /* baseline resets */
  background: transparent;
  border-width: 0;
  border-radius: 0;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-block;
  font-size: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  margin: 0;
  padding: 0;
  text-align: left;
  text-decoration: none;
  /* end of baseline resets */

  /* additional styles for the demo */
  display: block;
  font-family: Arial, sans-serif;
  padding: 10px 1.25em 10px 8px;
  position: relative;
  width: 100%;

.wf-accordion__trigger::after {
  content: '';
  border: solid #929292;
  border-width: 0 2px 2px 0;
  height: 0.5em;
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-60%) rotate(45deg);
  width: 0.5em;

.wf-accordion__trigger[aria-disabled=true] {
  cursor: not-allowed;
  opacity: 0.5;

.wf-accordion__trigger[aria-expanded=true]::after {
  transform: translateY(-30%) rotate(-135deg);

.wf-accordion__trigger:focus {
  background: #f5f5f5;
  color: #161616;

.wf-accordion__trigger:focus::after {
  border-color: #161616;

.wf-accordion__panel {
  background-color: #fff;
  padding: 10px 8px;

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

Available configurations

  // default selectors
  accordionGroup: '.js-accordion-group',
  accordionRoot: '.js-accordion',
  accordionHeader: '.js-accordion__header',
  accordionTrigger: '.js-accordion__trigger',
  accordionPanel: '.js-accordion__panel',
  // disables the automated hash update when triggering an accordion
  disableHashUpdate: false,


Next: Best 10 ways to send newsletters

also view other related posts,