Accordeon

Een accordeon is perfect om veel content compact te presenteren. Het is een verzameling knoppen waarmee je content kunt tonen of verbergen. Gebruikers kunnen zelf kiezen welke informatie ze willen zien.

Gebruik een accordeon:

  • Bij lange pagina’s met veel informatie (FAQ’s, handleidingen)
  • Om gebruikers te laten kiezen welke content ze willen zien

Belangrijke onderdelen:

  • Container: Dit is het element waar de accordeon in is opgebouwd.
  • Titel: Dit is het label van een sectie. Het is gelijk ook een bedieningselement om de content te tonen of verbergen.
  • Paneel: Dit is de sectie met content die hoort bij het label.

Technische opbouw

  • Maak elke titel een <button>-element
    • Plaats deze knop in een kop-element (<h1> tot en met <h6>) van het juiste niveau
  • Geef elke titel een naam via zichtbare tekst
  • Voor uitgevouwen panelen:
    • Geef de knop aria-expanded="true"
  • Voor samengevouwen panelen:
    • Geef de knop aria-expanded="false"
    • Verberg de content met hidden
  • Geef elk paneel een uniek ID
  • Koppel de knoppen via aria-controls met het ID van het bijbehorende paneel

Optionele verbeteringen

  • Maak de container een <section>-element
  • Geef de container een naam
  • Geef elk paneel role="region"
    • Let op: Vermijd dit bij accordeons met meer dan 6 panelen die tegelijk open kunnen
    • Geef het dan ook een naam via aria-labelledby, gebruik het ID van de bijbehorende knop

Voorbeeld accordeon

HTML
<!-- Accordeon code -->
<section class="accordion" aria-label="Veelgestelde vragen">
  <div class="accordion-item">
    <h3>
      <button class="accordion-button" aria-expanded="false" aria-controls="acc-panel-1">
        Wat is toegankelijkheid?
      </button>
    </h3>
    <div class="accordion-content" id="acc-panel-1" hidden>
      <p>
        Toegankelijkheid zorgt dat websites bruikbaar zijn voor iedereen, inclusief mensen met een beperking.
      </p>
    </div>
  </div>
  <div class="accordion-item">
    <h3>
      <button class="accordion-button" aria-expanded="false" aria-controls="acc-panel-2">
        Waarom is WCAG belangrijk?
      </button>
    </h3>
    <div class="accordion-content" id="acc-panel-2" hidden>
      <p>
        WCAG biedt richtlijnen om digitale content toegankelijk te maken. Dit vergroot de doelgroep en verbetert de gebruikerservaring.
      </p>
    </div>
  </div>
</section>
CSS
/* Accordeon stijlen */
.accordion {
  border: 1px solid;
  border-radius: 4px;
}

.accordion-item {
  border-bottom: 1px solid;
}

.accordion-item:last-child {
  border-bottom: none;
}

.accordion-button {
  width: 100%;
  text-align: left;
  color: white;
  background: black;
  border: none;
  padding: 1rem;
  font-size: 1.1rem;
  cursor: pointer;
  position: relative;
}

.accordion-button:hover {
  color: black;
  background: white;
}

.accordion-button::after {
  content: '+';
  position: absolute;
  right: 1rem;
  font-size: 1.2rem;
}

.accordion-button[aria-expanded="true"]::after {
  content: '−';
}

.accordion-content {
  padding: 1rem;
}

.accordion-content[hidden] {
  display: none;
}
JavaScript
// Accordeon functionaliteit
class Accordion {
  constructor(domNode) {
    this.rootEl = domNode;
    this.buttons = this.rootEl.querySelectorAll('.accordion-button');
    this.panels = new Map();

    // Maak een map van buttons naar panels
    this.buttons.forEach(button => {
      const controlsId = button.getAttribute('aria-controls');
      const panel = document.getElementById(controlsId);
      this.panels.set(button, {
        contentEl: panel,
        open: button.getAttribute('aria-expanded') === 'true'
      });
    });

    // Voeg event listeners toe
    this.buttons.forEach(button => {
      button.addEventListener('click', () => this.onButtonClick(button));
      button.addEventListener('keydown', (e) => this.onKeyDown(e, button));
    });
  }

  onButtonClick(button) {
    const panel = this.panels.get(button);
    this.toggle(button, !panel.open);
  }

  onKeyDown(e, button) {
    const buttonArray = Array.from(this.buttons);
    const currentIndex = buttonArray.indexOf(button);
  }

  toggle(button, open) {
    const panel = this.panels.get(button);

    // Stop als de staat niet verandert
    if (open === panel.open) {
      return;
    }

    // Update de interne staat
    panel.open = open;

    // Update de DOM
    button.setAttribute('aria-expanded', `${open}`);
    if (open) {
      panel.contentEl.removeAttribute('hidden');
    } else {
      panel.contentEl.setAttribute('hidden', '');
    }
  }
}

// Initialiseer accordions
const accordions = document.querySelectorAll('.accordion');
accordions.forEach(accordionEl => {
  new Accordion(accordionEl);
}); 

Toetsenbordbediening

  • Enter of Spatie:
    • Bij een samengevouwen paneel: opent het paneel
    • Bij een uitgevouwen paneel: klapt het paneel in
    • Als er maar één paneel open mag zijn: sluit het andere paneel
  • Tab:
    • Gaat naar het volgende focusbare element in de accordeon of zichtbaar paneel
  • Shift + Tab:
    • Gaat naar het vorige focusbare element in de accordeon of zichtbaar paneel

Optionele toetsen

  • Pijltjestoetsen:
    • Pijl omlaag: gaat naar de volgende accordeon titel
    • Pijl omhoog: gaat naar de vorige accordeon titel
  • Home:
    • Gaat naar de eerste accordeon titel
  • End:
    • Gaat naar de laatste accordeon titel

Focus management

  • Na het openen van een paneel blijft de focus op de knop

Beoordeling

Component

  • (4.1.2) De titel van de accordeon moet een <button>-element zijn
  • (1.3.1) De knop zou in een kop-element moeten staan (<h1> t/m <h6>)
  • (1.3.1) De knop moet moet een toegankelijke naam hebben
    • (2.4.6) De toegankelijke naam moet beschrijven wat er in het bijbehorende paneel staat
  • (4.1.1) De knop moet een uniek ID hebben
  • (4.1.1) Elk paneel moet een uniek ID hebben
  • (1.3.1) Elke knop zou aria-controls moeten hebben met het ID van het bijbehorende paneel
  • (4.1.2) Uitgevouwen panelen moeten aria-expanded="true" hebben
  • (4.1.2) Samengevouwen panelen moeten aria-expanded="false" hebben
  • (4.1.2) Samengevouwen panelen moeten verborgen zijn

Bronnen