Accordion

Recipe 8.4 Create groups of disclosure widgets

Sample:

Frequently asked questions

First question

First answer…

Second question

Second answer…

Code:

<style>
 .faq [aria-expanded] {
   all: unset;
   cursor: pointer;
}

 .faq [aria-expanded]:focus-visible {
  outline: 4px solid; 
}

 h3:has([aria-expanded="false"]) + .faq-content {
  display: none;
}
</style>

<section aria-labelledby="faq_heading" class="faq">
  <h2 id="faq_heading">
    Frequently asked questions
  </h2>

  <h3>
    First question
  </h3>
  <div class="faq-content">
    <p>
      First answer…
    </p>
  </div>
  
  <h3>
    Second question
  </h3>
  <div class="faq-content">
    <p>
      Second answer…
    </p>
  </div>
 
  <!-- 
    repeat for as many accordion panels as needed
  -->
</section>

<script>
  const faq = document.querySelector(' .faq')
  const headings = faq.querySelectorAll('h3')

  for (let i = 0; i < headings.length; i++) { 
    const button = document.createElement('button') 
    const heading = headings[i]
    const content = heading.nextElementSibling
    const id = `faq_${i}`; 
    
    button.setAttribute('aria-expanded', false) 
    button.setAttribute('aria-controls', id)
    button.textContent = heading.textContent
    heading.innerHTML = '' 
    heading.append(button)
    
    content.setAttribute('id', id) 
  }

  faq.addEventListener('click', e => { 
    const button = e.target.closest('[aria-expanded]')
    const isOpen = button.getAttribute('aria-expanded') === "false"

    if (button) {
      button.setAttribute('aria-expanded', isOpen)
    }
  })
</script>