We'd like to set Google Analytics cookies to learn how people use the Knowledge Base, so we can improve it. We set them only if you accept.

Skip to main content
Digital Accessibility Knowledge Base

Accordion

UI pattern

An accordion stacks disclosures, each of which can sit under a heading. An accessible accordion is built from accessible disclosures, and when it uses headings, they share one level and fit the page's heading outline.

An accordion lets users reveal only the sections they need, so a page with a lot of content takes less scrolling.

An accordion's sections can have headings or not, and it can let several sections be open at once or only one. This entry covers all of them. Headings put each section's title in the page's outline, so users can move between sections by heading.

Each section follows the disclosure pattern, and this entry carries the criteria a section's button and panel are tested on, along with what an accordion adds: the heading around each section's button and its level, the panel's role and name when its content earns a landmark, and the structure of the container. When the sections have headings, this entry recommends a heading element containing a button.

When to Use

Use an accordion when:

  • The page has several related sections of content, with or without headings, and users benefit from showing only the ones they need rather than scrolling past all of them.

Give each section a heading, as this entry describes, when:

  • Each section's title is meaningful as a heading and the sections belong in the page's outline, so users can reach them by moving from heading to heading.

A stack whose sections don't need headings is still an accordion, and this entry covers it too.

Do not use an accordion when:

  • There's only one collapsible section. Use a disclosure.
  • The sections are steps in a sequential process rather than independent topics. Use a pattern built for a sequence, such as a multi-step form.
  • Hiding the content would bury information every user needs immediately, such as critical form fields or error messages. Show it.

Examples

Code in this section is meant to demonstrate techniques rather than requirements and lacks features needed in a production application.

The examples show techniques for an accordion whose sections have headings. For sections without headings, see the examples in Disclosure.

Accordion Using ARIA Disclosure With Buttons

The HTML presents the content in full, with each heading followed by a <section> that references its heading through aria-labelledby. That structure becomes an accordion as a progressive enhancement when JavaScript is enabled.

Using <section> elements with an aria-labelledby attribute gives each the region role and includes it in the list of page landmarks. The example shows the technique, but listing the sections among the page's landmarks is a design decision that short descriptions like these wouldn't normally warrant.

The example includes a technique to collapse other disclosures when one is expanded.

Techniques:

  • The accordion builds on the ARIA Disclosure UI pattern, wrapping each controlling <button> element with a section heading.
  • Using HTML <button> elements as the disclosure controls makes them natively accessible, so they need only click event listeners.
  • The accordion headings are all at the same level, one level below the heading that introduces them.
  • After the text of each button that JavaScript adds, CSS draws an icon indicating whether the section is open or closed. The icon follows the text so the heading text, which is rendered before the JavaScript runs, doesn't shift.
  • To avoid layout shifts when JavaScript builds the accordion, CSS controls panel display depending on the availability of JavaScript, which is detected early. JavaScript then toggles the class is-open on each panel.
ARIA accordion HTML
HTML
<h2>Popular Pets</h2>


<div class="accordion" data-accordion>
  <h3
    id="heading-1"
    class="accordion-header"
  >
    Cats
  </h3>
  <section
    class="accordion-panel"
    aria-labelledby="heading-1"
  >
    A cat is a small, carnivorous mammal often adopted by a human family
    because it is cute, does funny things, and purrs. They are often found
    resting on laps, walking on laptops, or half-hiding around a corner
    while peering at you.
  </section>


  <h3
    id="heading-2"
    class="accordion-header"
  >
    Dogs
  </h3>
  <section
    class="accordion-panel"
    aria-labelledby="heading-2"
  >
    A dog is a vociferous omnivore descended from wolves that long ago
    domesticated humans after coaxing them to share food. The size and
    shape of dogs vary widely but all enjoy playing with tennis balls.
  </section>
</div>
ARIA accordion CSS

The scripting media query matches in modern browsers with JavaScript enabled in user settings even when JavaScript is otherwise blocked. To cover that case, add a <script> element to the HTML <head> that sets a class on the document root, then scope the CSS with a selector.

HTML
<!-- Add to <head> to condition availability of JavaScript -->
<script>document.documentElement.classList.add('js')</script>
CSS
@media (scripting: enabled) {
 :root.js {
   & .accordion-header > button {
     width: 100%;
     display: flex;
     justify-content: space-between;
     cursor: pointer;
   }
   & .accordion-header > button[aria-expanded]::after {
     display: block;
     content: "⌃" / "";
     transform: rotate(0.5turn);
   }
   & .accordion-header > button[aria-expanded="true"]::after {
     transform: rotate(0);
   }
   & .accordion-panel {
     display: none
   }
   & .accordion-panel.is-open {
     display: block
   }
 }
}
ARIA accordion JavaScript
JavaScript
class Accordion {
  #root
  #buttons = []


  constructor(root) {
    this.#root = root


    this.#root.querySelectorAll(':scope .accordion-header')
      .forEach((header) => {


        // create a button for each header
        // setting type=button to prevent accidental form submission
        const button = document.createElement('button')
        button.type = 'button'
        this.#buttons.push(button)


        // wrap header contents
        button.append(...header.childNodes)
        header.append(button)


        // derive panel id from header id
        const panelId = header.id + '-panel'
        header.nextElementSibling.id = panelId


        // set aria attributes
        button.setAttribute('aria-expanded', 'false')
        button.setAttribute('aria-controls', panelId)


        // add event listener to handle state changes
        button.addEventListener('click', () => {
          const open = button.getAttribute('aria-expanded') !== 'true'
          button.setAttribute('aria-expanded', String(open))


          // toggle 'is-open' class on panel button controls
          this.#root.querySelector(`#${button.getAttribute('aria-controls')}`)
            .classList.toggle('is-open', open)


          // close others
          this.#buttons.forEach((btn) => {
            if (btn === button) return
            btn.setAttribute('aria-expanded', 'false')
            this.#root.querySelector(`#${btn.getAttribute('aria-controls')}`)
              .classList.toggle('is-open', false)
          })
        })
      })
  }
}


// After page loads
document.querySelectorAll('[data-accordion]').forEach((el) => new Accordion(el));

Accordion Using Details and Summary

Each section is a <details> element whose <summary> holds the section's heading. A heading inside the <summary> is the only way to combine a heading with <details>, because the <summary> has to be the first child of the <details> element. The browser toggles each section and carries its state in the open attribute, with no script or ARIA.

Giving every <details> element the same name value makes opening one section close the others, as the script in the ARIA example does.

The <details> element carries the group role, which doesn't create a landmark, and it accepts no other role, so a section built this way can't become a region as the panels in the ARIA example do.

The heading's text names the <summary>, but whether the heading is also reported as a heading, with a role and a level, varies between assistive technologies. That's why this entry recommends the ARIA construction for an accordion with headings.

Native accordion HTML
HTML
<h2>Popular Pets</h2>


<details name="pets">
  <summary>
    <h3>Cats</h3>
  </summary>
  <p>
    A cat is a small, carnivorous mammal often adopted by a human family
    because it is cute, does funny things, and purrs. They are often found
    resting on laps, walking on laptops, or half-hiding around a corner
    while peering at you.
  </p>
</details>


<details name="pets">
  <summary>
    <h3>Dogs</h3>
  </summary>
  <p>
    A dog is a vociferous omnivore descended from wolves that long ago
    domesticated humans after coaxing them to share food. The size and
    shape of dogs vary widely but all enjoy playing with tennis balls.
  </p>
</details>

Structure and Semantics

A section of an accordion with headings is two things. It's a disclosure, a button that shows and hides a panel, and the tests below set out what that takes. It's also a heading that wraps the button at a level fitting the page's outline. The button is the only thing inside the heading, and the panel follows the heading in the DOM.

A panel is a named region only when its content earns a landmark, as Panel Role describes.

Build the heading around a button rather than inside a <summary>. HTML allows a heading inside a <summary>, and that's the only way to combine a heading with a <details> element. The <summary> has to be the first child of <details>, so it can't sit inside a heading.

Support for a heading inside a <summary> is thin: its text still names the control, but whether it's also reported as a heading, with a role and a level, varies. Being reported as a heading is why the heading is there, so that construction falls short at the one thing it's for.

An accordion already built from <details> elements still has working disclosures. The recommendation is about where the heading goes. For the native construction, see Accordion Using Details and Summary.

Accordion Has Disclosures

Every section of an accordion is a disclosure first, with or without a heading. A section whose control doesn't report that it expands content leaves users with a list of titles and content they have no reason to look for.

Contains valid disclosures

Reported as: Accordion has no disclosures

Each section of an accordion must be a disclosure whose control reports that it expands content and whether that content is showing.

When the sections expand and collapse only visually, with plain elements as controls and no aria-expanded anywhere, screen reader users hear the section titles and nothing else. They can't tell that a section holds hidden content, that its title is operable, or which sections they've already opened.

Give each control the button role and an aria-expanded attribute that tracks its panel. A <details> element with a <summary> meets this too, as Accordion Using Details and Summary shows. Even so, a section's heading belongs around a button rather than inside a <summary>.

For what aria-expanded has to do, see aria-expanded.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.

Headings Structure

When a heading is needed, each section's heading wraps its button, and the button is the only thing inside it. Anything else that sits alongside a section's title, such as an icon, a badge, or a menu button for actions on the section, goes beside the heading rather than inside it.

Wraps controls in headings

Reported as: Accordion breaks heading hierarchy

In an accordion whose sections need headings, each section's button should be wrapped in a heading, and should be the only thing inside it.

When a section's button has no heading around it, the section's title is missing from the page's outline, and screen reader users can't move between the sections by heading. When the heading also holds other elements, such as a badge or a menu button, assistive technology reports their content as part of the section's title, so screen reader users hear it every time they reach the section.

Wrap each button in a heading and move anything else beside it. For a section built from <details> and <summary>, rebuild it as a heading wrapping a button rather than adding a heading inside the <summary>.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

Headings Level

When a heading is needed, each section's heading carries a level that fits where the accordion sits in the page's outline, and every section in one accordion uses the same level. Count the level from the headings around the accordion rather than from the accordion alone: under an <h2>, the sections are <h3> elements. A native <h2> to <h6> element carries its level in the tag, while role="heading" needs aria-level alongside it.

Gives ARIA headings a level

Reported as: ARIA heading missing aria-level

A section heading built with role="heading" should declare its level with aria-level.

When the level is missing, the accordion's sections appear in the page's outline at a default depth nobody chose. Screen reader users who move through the page by heading get a structure that doesn't match how the content is organized.

Add aria-level set to the depth the sections sit at, one below the heading that introduces the accordion. Better, use a native heading element, which carries its level in the tag.

Severity: Moderate

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.
Uses consistent heading levels

Reported as: Accordion has mixed heading hierarchy

Every section heading in one accordion should use the same level.

When the levels differ, the outline tells screen reader users that some sections are subsections of others, although the sections are peers. The mistake is invisible on screen, where the headings usually look identical.

Choose the level once for the whole accordion, to fit where it sits in the page's outline, and apply it to every section from one place in your code. If a section's content nests, put the nested headings inside its panel rather than varying the levels of the sections.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

Role

An accordion section with a heading has two elements with one role each: the heading carries the heading role, and the button inside it carries the button role. Putting role="button" on the heading, or role="heading" on the button, merges the two elements the pattern keeps apart, and the section loses either its place in the outline or its control.

When a panel's content earns a landmark, the panel carries the region role too, as Panel Role and Accessibility Label describe.

Has the correct role

Reported as: Element has incorrect role

A section's button must carry the button role.

When the element users operate in the heading has no role, or the wrong one, assistive technology reports the section's title as plain text or as some other kind of element. Screen reader users can't tell that the title opens anything.

Use an HTML <button> inside the heading, which carries the role on its own, or set role="button" on the element users operate.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Applies role to the correct element

Reported as: Incorrect root element

Sanity check. Evinced checks that the button role sits on the selected element, the one users focus and operate in each section, rather than on one of its descendants. This check runs on every UI pattern, not only on accordions.

A section's button role should sit on the same element that receives focus and handles activation.

When the role sits on a descendant while a wrapper takes focus or handles the click, screen readers announce a button that isn't the element users operate, and keyboard users may reach a control that doesn't open the section.

Put the role, the accessible name, focusability, and the activation handling on that one element, and keep any wrapper free of roles and handlers. If the test selected a wrapper by mistake, select the section's button itself and run the test again.

Panel Role

A panel needs a role only when its content earns a landmark: content important enough that users will want to reach it directly and find it listed in a summary of the page. That's most often a panel holding headings of its own or a nested accordion.

When a panel earns one, make it a <section> element and name it with aria-labelledby pointing at its section's heading, as Accessibility Label describes. A named <section> has the region role implicitly, so it needs no role attribute.

A <details> element carries the group role, which isn't a landmark, and it accepts no other role. When a section built from <details> has content that earns a landmark, build it as a heading wrapping a button instead.

Gives panels a region role

Reported as: Accordion panel is not a section element

An accordion panel can be a named region, which makes it a landmark users can reach from the list of the page's landmarks.

When a panel holds substantial content, such as headings of its own or a nested accordion, a landmark lets users go straight to it. When a panel holds a few lines under its heading, a landmark only lengthens the list users have to read through.

Give the panel the region role only when its content earns one. Make it a <section> named by aria-labelledby pointing at its section's heading, and leave other panels without a role.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

Button Name

Each section's button needs an accessible name, and that name is all users have to go on before they open anything, so it names the section rather than the act of opening it. In an accordion with headings, the heading's text is the button's text.

In a section built from a <details> element, the <summary> element is the control and takes its name from the text it contains.

A section's button takes its name from the text inside it. When its visible content is an icon rather than text, the name comes from the icon's alternative text, from an aria-labelledby pointing at visible text elsewhere, or from an aria-label when no such text is available.

Has an accessible name

Reported as: Accessible name

A section's button must have an accessible name that says which section it reveals.

When the button holds only an icon, a chevron, or an image with empty alt text, the section's title sits beside it, outside the button, and users get a control they can operate and can't identify. Every section's button then exposes the same name, so choosing between them means opening each one to find out what it was.

Name the button from the text inside it. When the design leaves no visible text to use, fall back to meaningful alt text on a child image, an aria-label, or an aria-labelledby pointing at text elsewhere on the page.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Puts the name in the summary text

Reported as: Summary has no accessible name

In a section built from a <details> element, the <summary> must contain text that says which section it reveals.

When the <summary> is empty, or holds only an icon or an image with empty alt text, users get a control they can operate and can't identify. Every section's summary then exposes the same name or none, so choosing between them means opening each one.

Put the text that names the section inside the <summary>. When the design leaves no visible text to use, give a child image meaningful alt text.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Details has summary with an accessible name

Reported as: Native disclosure is missing summary element

A section built from a native <details> element must have a <summary> element with an accessible name as its first child.

When the first child of the <details> element is the content itself, the browser still renders a control and falls back to a default string, usually "Details". Users get a stack of identically named controls and no way to tell which one holds what they came for.

Put a <summary> first inside each <details> element and give it the text that names the section. When the section needs a heading, build it as a heading wrapping a button instead.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.

Accessibility Label

When the panel carries a role, it should be labeled by its section's heading, whose text is also the button's text, so users who arrive at the panel can tell which section they're in. This is about the panel, not the button's own name.

Point the panel's aria-labelledby at the heading's id, or use aria-label when there's no heading text to point at. Point at the heading rather than the button: it carries the same text, and it's there before any script adds the button, as the example shows.

The attributes work only when the panel has a role that accepts a name, and in an accordion that's the region a <section> becomes once it's named, as Panel Role describes. Most panels don't earn a region, so most don't need a name. When a panel is a region, it needs a name, and these attributes supply it. The role and the name go together in both directions.

Names the panel from its heading

Reported as: Disclosure panel has no accessible name

An accordion panel that carries a role should take its accessible name from its section's heading.

When a panel has a region or group role and no name, assistive technology reports it as a region with nothing attached, or as an unnamed group it may skip. Users who move into the panel, or who arrive at it by jumping between regions, are reading a section with no indication of which part of the accordion it belongs to.

Give each section's heading an id, and point the panel's aria-labelledby at it. Give a panel a role only when its content earns one, and don't put the attribute on a panel with no role, where it names nothing.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

aria-expanded

Users need to know whether a section's content is showing. This test covers whether each section reports its state and whether the report is right. Whether it changes when the section is operated belongs to Toggle Activation.

A section built from a <details> element reports its state through the open attribute. Neither <details> nor its <summary> takes aria-expanded, which would add a second report of the state with nothing keeping the two in step.

A section's button carries aria-expanded instead, in the collapsed state as well as the expanded one, with the value tracking whether its panel is showing.

Reflects aria-expanded state

Reported as: Conflicting aria-expanded value

The aria-expanded value on a section's button must match whether its panel is showing: false while the panel is hidden and true while it's showing.

When the value is derived from something other than the panel's real state, it reports the opposite of what's on screen: users skip a section that's open in front of them, or go looking for content that was never revealed.

Set the attribute from the same state that decides whether the panel renders.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.
Updates aria-expanded

Reported as: Aria-expanded value did not change

The aria-expanded value on a section's button must change whenever its panel opens or closes.

When the panel changes and the value doesn't, the attribute goes on reporting the state the panel was in before, so screen reader users are told a section is closed while it's showing, or the reverse. In an accordion that closes other sections when one opens, the buttons of the closed sections need updating too.

Change the attribute in the same step that shows or hides the panel, whatever caused it to open or close.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.
Has aria-expanded

Reported as: Missing aria-expanded attribute

A section's button must carry aria-expanded, in the collapsed state as well as the expanded one.

When the attribute is absent, nothing in the markup says the button reveals anything, so screen reader users have no reason to expect content to appear and can't tell afterward whether activating it worked.

Set it to false from the start rather than adding it once the panel has been opened.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.

Disabled

An accordion's disabled state belongs on a section's button, never on its heading or its panel. Users can always collapse the section they've opened. An accordion that keeps one section open never disables that section's button to do it, although the APG accordion pattern allows aria-disabled there.

A section's button is disabled only when its content isn't available yet, the same rule as for any disclosure.

Disables only when role allows

Reported as: Component does not support disabled state

An accordion's disabled state must be set only on a section's button, never on its heading, its panel, or the accordion's container.

When disabled or aria-disabled is set on an element whose role has no disabled state, assistive technology may drop it or report the element as something it isn't, and the buttons inside stay fully operable. Nothing users can reach is actually disabled.

Take the attribute off the heading, panel, or container, and set it on the button of each section that is unavailable.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Disables only elements that support it

Reported as: Element type does not support disabled state

An element in an accordion must carry disabled or aria-disabled only when its role supports a disabled state.

When the state sits on an element whose role has none, such as a wrapper, a list item or a heading, assistive technology may drop it or report it on something users can't operate, while the control that really is unavailable is still reported as available.

Remove the attribute from that element, and set the disabled state on the control itself, using an element or role that supports it, such as a native <button>.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Avoids conflicting disabled states

Reported as: Conflicting disabled values

A section's button must declare its disabled state one way only.

When the button carries the disabled attribute alongside an aria-disabled value that disagrees with it, assistive technology may report a different state than the browser enforces, so users can't tell whether the section will open.

Use disabled on a native <button> and aria-disabled on a button built from another element, never both.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Uses aria-disabled rather than disabled

Reported as: Element does not support the disabled attribute

A section's button built from an element that doesn't support the disabled attribute must convey its disabled state with aria-disabled.

When disabled is set on such an element, browsers ignore it, and the button stays an available control in the accessibility tree however grayed out it looks. Screen reader users are told they can open a section the design means to be unavailable.

Set aria-disabled="true" instead, and keep it in step with the code that stops the button from toggling its panel. A native <button> avoids the problem, since disabled then does all of it.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.

Forbidden Interactive Children

A section's button holds the section's title and nothing users operate. Nested interactive elements create ambiguity for all users, who can't be certain which element will respond to their interaction.

Avoids nested interactives

Reported as: Forbidden interactive children; Nested interactive elements

A section's button must not contain or be nested inside another interactive element, such as a link or another button.

A button has no accessible children. An interactive element nested inside it is presentational, so screen reader users can't perceive or operate it as a control.

Move the interactive element out of the button. A control for actions on the section, such as a menu button, goes beside the heading rather than inside it.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.

Relationships

An accordion owns its sections, and they sit in the DOM in order inside the accordion's container: each heading, then its panel. Each section's button controls the panel that follows its heading, showing and hiding it.

aria-controls

As a best practice, each section's button should carry aria-controls naming its panel. The panel follows its heading directly, so users reach the content by reading on, and the attribute states the relationship for the assistive technology that uses it.

Controls a single target

Reported as: Aria-controls has multiple targets

A section's button should name only its own panel in aria-controls.

When the attribute lists several ids, such as every panel in an accordion where opening one section closes the others, the button no longer says which content it shows. Assistive technology that offers to move users to the controlled element has to pick one or offer none.

Point each button's aria-controls at its own panel. Closing the other sections is a side effect of opening this one, not something the button controls.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.
References valid aria-controls

Reported as: Invalid aria-controls reference

A section's button must point aria-controls at a panel that exists on the page.

When the id matches nothing, because it's misspelled or names a panel that hasn't been rendered, assistive technology finds no target and can't tell users what the button affects, while the markup still looks correct.

Generate each panel's id and its button's aria-controls value from the same source. When a panel exists only while it's open, add the attribute when the panel appears and remove it when the panel goes.

Severity: Minor

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.
Sets aria-controls

Reported as: Missing aria-controls attribute

A section's button should name its panel with aria-controls.

When nothing connects the button to its panel, the relationship exists only in the layout and the script. Assistive technology that uses the attribute can't tell users which content the button shows, or offer to move them to it.

Give each panel a unique id, and set its button's aria-controls to that id.

Severity: Minor

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.

aria-owns

An accordion holds its sections as DOM descendants of its container, each heading followed by its panel, in the order users meet them. That nesting states the whole structure, so an accordion has no reason to claim its sections with aria-owns.

Each section's button has no reason to own anything either: it reaches its panel through aria-controls.

Avoids aria-owns

Reported as: Avoid aria-owns

General check. Evinced checks that an accordion and its sections' buttons don't use aria-owns to claim headings, panels, or other elements. This check runs on every UI pattern, not only on accordions.

An accordion and its sections' buttons should not use aria-owns.

The headings and panels already sit inside the container in reading order. When an accordion or a section's button owns them anyway, assistive technology can present a panel away from its heading, and screen reader users meet content with no indication of which section it belongs to.

Remove aria-owns and keep each heading and its panel in the container, in order. A section's button names its panel with aria-controls, not aria-owns.

Owns only conforming roles

Reported as: Invalid aria-owns values

General check. Evinced checks that every element an accordion or a section's button claims with aria-owns has a role its owner accepts. This check runs on several UI patterns.

When an accordion or a section's button uses aria-owns anyway, each element it references should have a role its owner accepts.

An accordion's container has no role, so no owned element has a role it can accept, and a section's button treats everything inside it as presentational. When either claims headings, panels or other buttons, assistive technology can drop them or present them out of place, and screen reader users lose track of which panel belongs to which heading.

Remove aria-owns, and keep each heading and its panel inside the container in reading order, as the section intro describes.

Operation

Each section of an accordion is operated as a disclosure. With a pointer, clicking or tapping a section's button toggles its panel. With a keyboard, the Tab key moves focus to each button in turn, and Enter or Space toggles its panel. Focus stays on the button either way, and an open panel's content joins the tab order after its button.

An accordion's panels sit in the page flow and push the content below them down, so they don't close with Escape. Evinced checks for Escape only on panels that open over other content.

An accordion is a set of separate tab stops, one for each button, not a composite widget with a single tab stop. The arrow keys aren't part of the pattern, so don't add them to move between sections. Screen reader users can move between the sections by heading instead.

In an accordion that allows only one open section, opening a section closes the one that was open, and users can still collapse the open section directly.

Focus Sequence

Each section's button is a single tab stop, reachable with the Tab key whenever it's enabled, and opening a panel doesn't change that. Hide a closed panel with the hidden attribute or display: none rather than moving it off screen, so its content leaves the tab order with it and keyboard users don't land inside content nobody can see.

Reachable by keyboard

Reported as: Keyboard accessible

A section's button must be reachable with the Tab key.

When the button is built from markup that isn't focusable, such as a <div> or a <span> given click handling and a role, keyboard users can't reach it, so the section it reveals is out of their reach too.

Use a <button> element, which is focusable on its own, or a <summary> in a section built from <details>. When the button has to stay generic, add tabindex="0" so it joins the tab order in document order.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.22.1.1 Keyboard (A)All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.
EN 301 5499.2.1.1 KeyboardSame as the WCAG text.
Section 5081194.21(a)When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually.
508 Trusted Tester2.1.1-keyboard-access, 4.AAll functionality can be accessed and executed using only the keyboard.
Skips tab order when disabled

Reported as: Disabled element focusable

A section's button that presents as disabled should not be a stop in the tab order.

When Tab still lands on a disabled button, because a native <button> was styled as disabled without the disabled attribute or a generic one kept tabindex="0", keyboard users arrive at a button that reveals nothing.

Use the disabled attribute on a native <button>, which removes it from the tab order. On a generic button, set tabindex="-1" while it's disabled and restore tabindex="0" when it's available again. If you keep it focusable so users can hear why it's unavailable, make sure assistive technology reports the disabled state and activation is blocked.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.
Puts focus on element with button role

Reported as: Focusable offset

The element that carries a section's button role must be the element that receives focus, not one of its descendants.

When the role sits on a wrapper and focus lands on a child inside it, screen reader users tabbing through the accordion hear nothing about what they've landed on, and can't tell it's a button that reveals a section.

Make the element with the role focusable, with a <button> element or tabindex="0", and remove focusability from the descendant that was taking focus.

Severity: Minor

StandardSuccess CriterionReference
WCAG 2.22.1.1 Keyboard (A)All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.
EN 301 5499.2.1.1 KeyboardSame as the WCAG text.
Section 5081194.21(a)When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually.
508 Trusted Tester2.1.1-keyboard-access, 4.AAll functionality can be accessed and executed using only the keyboard.

Keyboard Activation

A section's button activates with either the Enter or the Space key, as any button does. A native <button> responds to both keys on its own, and so does a <summary>. A button built from a generic element has to be given that behavior.

Operable by keyboard

Reported as: Button not activated by keyboard

A section's button must show and hide its panel when keyboard users activate it.

When a button built from a generic element handles only the click event, keyboard users can focus it but can't open the section.

Use a native <button> element, which is keyboard operable on its own. Otherwise, add keyboard event listeners that either toggle the panel or create a click event that does.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.22.1.1 Keyboard (A)All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.
EN 301 5499.2.1.1 KeyboardSame as the WCAG text.
Section 5081194.21(a)When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually.
508 Trusted Tester2.1.1-keyboard-access, 4.AAll functionality can be accessed and executed using only the keyboard.
Ignores keyboard when disabled

Reported as: Disabled button keyboard activation

A section's button that reports itself as disabled should reject keyboard activation.

When a button carries aria-disabled="true" but the Enter or Space key still toggles its panel, screen reader users may be confused or lose trust in the interface.

Use a native <button>, which blocks interaction when disabled is set. Otherwise, check the disabled state in the keyboard handlers and return before toggling anything.

Severity: Serious

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.
Activates with Enter and Space

Reported as: Button requires space key activation

A section's button must activate with the Space key as well as the Enter key, as a <button> element does.

When only Enter works, users who press Space, as they would on any other button, may conclude the button is unresponsive.

Add a Space keyup handler that toggles the panel, or use a native <button> element.

Severity: Minor

StandardSuccess CriterionReference
WCAG 2.22.1.1 Keyboard (A)All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.
EN 301 5499.2.1.1 KeyboardSame as the WCAG text.
Section 5081194.21(a)When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually.
508 Trusted Tester2.1.1-keyboard-access, 4.AAll functionality can be accessed and executed using only the keyboard.

Toggle Activation

Activating a section's button toggles its panel and the state the button reports, on every activation and by every route: a pointer click, the Enter key, and Space. A button that presents as disabled does neither.

Updates aria-expanded when operated

Reported as: Element's state did not change

A section's button must update aria-expanded every time it's operated.

Nothing looks broken to sighted users, who watch the panel open and close, while screen reader users are told it's still in the state it started in. After a few presses with no change in what they hear, they decide the button is broken.

Derive the attribute from the same state that decides whether the panel shows, and handle pointer and keyboard activation through one path rather than two.

Evinced reports this issue as Critical or as Serious, depending on the context where it shows up.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Returns to its earlier state

Reported as: Toggle state not resetting

Activating a section's button twice must return aria-expanded to the value it started with.

When the code assigns a fixed value on activation instead of inverting the current one, the attribute stops tracking the panel after the first press. The panel goes on opening and closing, and screen reader users are told a closed section is open.

Invert the state on each activation rather than assigning a fixed value.

Evinced reports this issue as Critical or as Serious, depending on the context where it shows up.

Severity: Critical

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
When set, aria-expanded stays consistent with the open state

Reported as: Invalid toggle attribute

When a section built from a <details> element carries aria-expanded on the <details> or the <summary> element (not recommended), the value must stay consistent with the open attribute.

The browser derives the state from open while assistive technology reports the ARIA value to users, so the two disagree from the first toggle. Nothing in the native construction updates the attribute, so screen reader users go on being told that a section they've just opened is closed.

Remove aria-expanded and let the native state stand. When it can't be removed yet, update it from the toggle event on each <details> element so it at least tracks open.

Severity: Serious

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Ignores activation when disabled

Reported as: Disabled element active

A section's button that presents as disabled must not toggle its panel when it's activated.

aria-disabled="true" changes only what's reported, not what the browser allows, so the click and key handlers keep firing unless the code checks the state itself. Users are told the section isn't available and then find that it opens, and they can't tell whether they've broken something they were warned off.

Check the disabled state at the top of the handler and return before changing anything, covering the pointer and the keyboard paths. On a native button, use the disabled attribute, which stops activation in the browser.

Severity: Moderate

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.
Collapses its panel on a second press

Reported as: Unresponsive disclosure button

Activating an expanded section's button again must collapse its panel.

When the second activation does nothing, the panel stays open and the button gives no response. Users can't put the section away, and a button that works once and then stops reads as broken.

Handle every activation as a toggle: expand the panel when it's collapsed and collapse it when it's expanded, updating aria-expanded each time.

Severity: Minor

StandardSuccess CriterionReference
WCAG 2.24.1.2 Name, Role, Value (A)For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies.
EN 301 5499.4.1.2 Name, Role, ValueSame as the WCAG text.
Section 5081194.21(d)Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text.
Closes a panel without opening another

Reported as: Accordion panel does not close

Users should be able to collapse an expanded panel without expanding another, unless the accordion is designed to keep one panel open.

When a panel collapses only as another one opens, users can't return the accordion to its headings, and screen reader users hear a section stay open after they tried to close it.

Let a second activation of the same button collapse its panel. If the accordion is meant to keep one panel open, say so in visible instructions, and keep aria-expanded accurate on every button.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

Event Listener Type

When script in an accordion responds to keys, it listens for keydown or keyup. The keypress event is deprecated, and browsers don't fire it for many keys, including the arrow keys, Home, End and Escape.

Avoids the keypress event

Reported as: Deprecated keypress listener

An accordion's key handling should use keydown or keyup, not keypress.

When a handler listens for keypress, keys the browser doesn't send through that event go unanswered, and keyboard users find the accordion ignoring keys it's meant to respond to.

Replace each keypress listener with a keydown listener, and read the key from event.key.

Severity: Minor

This issue is classified as "Best Practice," meaning it isn't tied to a specific WCAG success criterion or any other regulation or standard; however it still affects the experience of some users. These issues fall outside formal compliance requirements, yet addressing them improves usability and accessibility in practice.

FAQ

Which WCAG success criteria do the accordion issues relate to?

Most accordion issues relate to 4.1.2 Name, Role, Value: a section's button with the button role and an accessible name, a <summary> first in each <details> element, sections that are valid disclosures, a disabled state set on the button and declared one way only, no interactive elements inside the button, a state that updates when the button is operated and stays consistent with open on a native section, and a button that collapses its panel when it's pressed again.

Under 1.3.1 Info and Relationships, each button's aria-expanded reflects whether its panel is showing, aria-controls is present and points at the panel, and a disabled button ignores activation. Under 2.1.1 Keyboard, each button can be reached with the Tab key, takes focus itself, and activates with Enter and Space.

The rest are best practices that map to no success criterion: giving ARIA headings a level, keeping heading levels consistent, wrapping each section's button in a heading, giving panels a region role only when their content earns a landmark, naming each panel from its heading, pointing aria-controls at a single panel, and keeping a disabled button out of the tab order and away from keyboard activation.

Why are some accordion issues marked as Critical and others as Minor?

Severity reflects how much an issue gets in users' way. Critical issues stop users outright: a section's button without the button role or a name, a section that can't be reached or opened by keyboard, or an aria-expanded value that's wrong or doesn't change when the button is operated.

Serious issues break the accordion for assistive technology users: a section that isn't a valid disclosure, a button with no aria-expanded or one whose value stays put when the panel opens or closes, a <details> element with no <summary>, a disabled state reported wrongly, an interactive element nested in a button, a stale aria-expanded on a native section, or a disabled button that still responds to the keyboard.

Moderate issues weaken navigation or trust, such as an ARIA heading with no level or a disabled button that still opens its section. Minor issues affect how easily users find their way, such as inconsistent heading levels, buttons without headings, panel roles and names, aria-controls that's missing or wrong, a disabled button left in the tab order, focus that lands inside a button, or a button that ignores the Space key.

Two checks carry no severity: the general check for aria-owns and the sanity check on where the button role sits.

  • Disclosure: the pattern each section follows, including its button's state and operation