🛠️ Accordion: Screen reader accessibility

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

🛠️ Accordion: Screen reader accessibility

The accordion component contains a series of headers and contents. We know there’s a button inside of each header. The purpose of the button is to open and close the contents.

Since contents are closed when the page loads, we should add aria-expanded="false" to all header buttons.

<div class="accordion" data-theme="cheese">
  <div class="accordion__header">
    <h2> <button aria-expanded="false"> ... </button> </h2>
  </div>
  ...
</div>
<!-- Repeat for other buttons -->

Expanding the content

When we open the contents, we need to set aria-expanded to true. This tells screen readers something has expanded.

const openAccordion = accordion => {
  // ...
  const accordionHeaderButton = accordion.querySelector('button')
  accordionHeaderButton.setAttribute('aria-expanded', 'true')
}

The screen reader user’s next action (likely to be “Next Item” or Tab) will bring them into the content. We don’t have to focus on the content.

When we close the contents, we need to set the aria-expanded back to false.

const closeAccordion = accordion => {
  // ...
  accordionHeaderButton.setAttribute('aria-expanded', false)
  accordionHeaderButton.focus()
}

Improving the contents

Here’s what Voiceover users hear when they press “Next Item”.

And here’s what NVDA users here when they press “Next Item”.

Notice Voiceover says “Cheese” at the SVG while NVDA skips the Cheese SVG completely? In this case, the image is a decorative image. We don’t want any screen readers to read it.

To ensure all screen readers skip the SVG, we can add aria-hidden to each SVG.

<!-- Repeat for other contents -->
<div class="accordion__inner">
  <svg ... aria-hidden="true"> ... </svg>
</div>

Grouping the content

Sighted users can see the contents of each accordion. They know where the contents begin and where it ends.

But a blind user can’t see where the content begins and ends. We can improve their experience by adding a group role and an accessible name.

<!-- Repeat for other contents -->
<div class="accordion__content" role="group" aria-label="Cheese"> ... </div>

Unfortunately, this works only for Voiceover. NVDA ignores the group role and the accessible name.

But this is not a waste!

It’s an enhancement for screen readers who speak a group role when labelled. Remember, we don’t need all screen readers to behave the same way. We simply need to make stuff accessible. Enhanced experiences are always welcome.

Moving between accordion headers

WCAG recommends we use ↑ and ↓ to switch between accordion headers. (We’ve built this functionality in the keyboard module).

Voiceover can use ↑ and ↓ to switch between accordion headers we expected.

However, NVDA users can’t. If they press ↓, they’ll move into the contents (assuming contents are open). Likewise, if they press ↑, they’ll move into the content (assuming the previous content is open).

This happens because their ↑ and ↓ are their “Previous Item” and “Next Item” keys in browse mode. We cannot overwrite these keys even if we used with event.preventDefault.

Luckily NVDA users can still switch between accordion headers with their H (next heading) and Shift + H (previous heading). This is why it’s so important to use a wrap an accordion header with a heading element.

That’s it!