ARIA
Verrijk de semantiek van complexe componenten om ze toegankelijk te maken
Wat is ARIA?
ARIA (Accessible Rich Internet Applications) is een W3C-specificatie die het mogelijk maakt de toegankelijkheid van rijke webtoepassingen en dynamische componenten te verbeteren.
🏷️ Rollen
Definieer wat een element is (knop, navigatie, waarschuwing...)
role="button"📊 Statussen
Beschrijf de huidige status van een element (aangevinkt, uitgeklapt, uitgeschakeld...)
aria-expanded="true"🔧 Eigenschappen
Bied aanvullende informatie (label, describedby, controls...)
aria-label="Hoofdmenu"⚠️ ARIA Gulden Regel
Gebruik geen ARIA als je native HTML kunt gebruiken
Formulieren met ARIA-attributen
Slecht voorbeeld
Formulier zonder ARIA
❌ Ontoegankelijk formulier zonder geschikte ARIA-attributen:
<!-- Slecht: geen label, geen beschrijving -->
<input type="text" placeholder="Je e-mail">
<!-- Slecht: icoon niet toegankelijk -->
<button>
<span class="icon">🔍</span>
</button>
<!-- Slecht: fout niet gekoppeld, geen autocomplete -->
<input type="password">
<div class="error">Wachtwoord te kort</div>Goed voorbeeld
Formulier met ARIA
✅ Toegankelijk formulier met geschikte ARIA-attributen:
<!-- Goed: aria-label voor veld zonder zichtbaar label -->
<input
type="text"
aria-label="E-mailadres (verplicht)"
aria-describedby="email-desc"
aria-required="true">
<span id="email-desc">Indeling: naam@voorbeeld.com</span>
<!-- Goed: knop met aria-label -->
<button aria-label="Formulier indienen">
<span aria-hidden="true">🔍</span>
</button>
<!-- Goed: fout gekoppeld met aria-describedby -->
<input
type="password"
aria-label="Wachtwoord (minimaal 8 tekens)"
aria-invalid="true"
aria-describedby="pwd-error"
autocomplete="new-password">
<div id="pwd-error" role="alert">
Wachtwoord moet minimaal 8 tekens bevatten
</div>Gebruikte ARIA-attributen:
aria-label: Biedt een toegankelijk labelaria-describedby: Koppelt een gedetailleerde beschrijvingaria-required: Geeft een verplicht veld aanaria-invalid: Signaleert een ongeldig veldaria-labelledby: Verwijst naar een bestaand labelrole="alert": Kondigt onmiddellijk fouten aanaria-hidden: Verbergt decoratieve elementenaria-live: Kondigt dynamische wijzigingen aan
Uitleg: ARIA-attributen verrijken formulieren door labels, beschrijvingen en statussen te bieden aan ondersteunende technologieën.
Toegankelijke accordeon
Slecht voorbeeld
Zonder ARIA-attributen
<!-- Slecht: Geen ARIA-attributen, geen toetsenbordnavigatie -->
<div class="accordion-header" @click="toggle">
<span>Title</span>
<span>+</span>
</div>
<div v-if="open">
Content
</div>Goed voorbeeld
Met juiste ARIA
<!-- Goed: Volledige ARIA met toetsenbordnavigatie -->
<button
id="accordion-button-1"
aria-expanded="false"
aria-controls="panel-1"
@click="toggle"
@keydown.enter="toggle"
>
Title
</button>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-button-1"
:hidden="!open"
>
Content
</div>Uitleg: Een accordeon moet geschikte ARIA-attributen gebruiken, toetsenbordnavigatie ondersteunen en statuswijzigingen aankondigen.
Tabindex en toetsenbordnavigatie
Het tabindex-attribuut bepaalt of een element toetsenbordfocus kan ontvangen en in welke volgorde.
✅ tabindex="0"
Element is focusbaar in de natuurlijke DOM-volgorde
tabindex="0"<!-- Goed: Interactief element focusbaar -->
<div role="button" aria-label="Custom action" tabindex="0">
Custom button
</div>⚠️ tabindex="-1"
Focusbaar via JavaScript maar niet via Tab
tabindex="-1"<!-- Goed: Voor programmatische focus -->
<div role="alert" aria-live="assertive" tabindex="-1">
Error message
</div>❌ tabindex > 0
Wijzigt navigatievolgorde (vermijden!)
tabindex="5"<!-- Slecht: Breekt natuurlijke tab-volgorde -->
<button tabindex="3">Third</button>
<button tabindex="1">First</button>
<button tabindex="2">Second</button>📋 Tabindex best practices
- Gebruik
tabindex="0"om aangepaste interactieve elementen focusbaar te maken - Gebruik
tabindex="-1"voor programmatische focus (modals, waarschuwingen) - NOOIT positieve waarden gebruiken - respecteer DOM-volgorde
- Native interactieve elementen (
<button>,<a>,<input>) hebben geen tabindex nodig
🎯 Interactieve voorbeelden
Test toetsenbordnavigatie (Tab/Shift+Tab) met deze elementen:
❌ Niet-focusbare elementen
Paragraaf
💡 Testtips
- Gebruik Tab om vooruit te navigeren
- Gebruik Shift + Tab om achteruit te navigeren
- Gebruik Spatie of Enter om elementen te activeren
- Observeer de focusindicator (blauwe omtrek)
- Let op welke elementen wel of niet focus kunnen ontvangen
Toegankelijk dropdown-menu
Slecht voorbeeld
Menu zonder ARIA
<!-- Slecht: Geen menu-rol, geen focus trap -->
<div @click="open = !open">
Actions ▼
</div>
<div v-if="open">
<div @click="action">Edit</div>
<div @click="action">Delete</div>
</div>Goed voorbeeld
Menu met ARIA
<!-- Goed: ARIA-attributen en focusbeheer -->
<button
aria-haspopup="true"
aria-expanded="false"
aria-controls="menu"
@keydown.escape="close"
>
Actions
</button>
<div
id="menu"
role="menu"
v-if="open"
>
<button role="menuitem">Edit</button>
<button role="menuitem">Delete</button>
</div>Uitleg: Een dropdown-menu moet toetsenbordnavigatie ondersteunen, sluiten met Escape en de focus correct behouden.
Toegankelijke afbeeldingengalerij
Slecht voorbeeld
Toegankelijke afbeeldingengalerij
<!-- ❌ Slecht: galerij niet toetsenbordtoegankelijk -->
<div class="thumbnails">
<img v-for="image in images"
:src="image.thumb"
@click="selectImage(index)"
:class="{ active: selected === index }" />
</div>
<div class="main-image">
<img :src="images[selected].full" />
</div>Goed voorbeeld
Toegankelijke afbeeldingengalerij
Vooraanzicht van de 16-inch MacBook Pro met gesloten scherm, met het strakke aluminium ontwerp.
<!-- ✅ Goed: toegankelijke galerij met toetsenbordnavigatie -->
<div role="tablist" aria-label="Productafbeeldingen">
<button v-for="(image, index) in images"
:id="`tab-${index}`"
role="tab"
:aria-selected="selected === index"
:aria-controls="`panel-${index}`"
:tabindex="selected === index ? 0 : -1"
@click="selectImage(index)"
@keydown="handleKeydown($event, index)">
<img :src="image.thumb" :alt="`${image.alt} - Miniature`" />
</button>
</div>
<div :id="`panel-${selected}`"
role="tabpanel"
:aria-labelledby="`tab-${selected}`">
<img :src="images[selected].full" :alt="images[selected].alt" />
<p>{{ images[selected].description }}</p>
</div>
<!-- Bonnes pratiques :
- Pattern tablist/tab/tabpanel ARIA
- Navigation avec flèches, Home, End
- Focus management avec tabindex
- Descriptions détaillées pour chaque image -->Uitleg: Een afbeeldingengalerij moet toetsenbordnavigatie mogelijk maken en contextuele beschrijvingen bieden voor elke afbeelding. Het ARIA tablist/tab/tabpanel patroon is perfect voor deze use case.
Toegankelijke sorteerbare tabel
Slecht voorbeeld
| Naam ↑ | Rol | Status | |
|---|---|---|---|
| Alice Martin | alice@example.com | Admin | Actief |
| Bob Dupont | bob@example.com | Gebruiker | Inactief |
| Charlie Leroy | charlie@example.com | Moderator | Actief |
| Diana Moreau | diana@example.com | Gebruiker | Geschorst |
Goed voorbeeld
| Alice Martin | alice@example.com | Admin | Actief |
|---|---|---|---|
| Bob Dupont | bob@example.com | Gebruiker | Inactief |
| Charlie Leroy | charlie@example.com | Moderator | Actief |
| Diana Moreau | diana@example.com | Gebruiker | Geschorst |
Uitleg: Een sorteerbare tabel moet de huidige sorteerstatus aangeven, toetsenbordnavigatie mogelijk maken en wijzigingen aankondigen aan schermlezers.
Dynamische aria-live regio's
Slecht voorbeeld
Zonder aria-live
Wijzigingen worden niet aangekondigd
<!-- Mauvais : pas d'attribut aria-live -->
<div class="status-banner">
<p></p>
</div>Teller: 0
Problemen:
- Wijzigingen worden niet aangekondigd
- Schermlezergebruikers weten niet dat de inhoud is gewijzigd
- Geen indicatie van berichtbelang
Goed voorbeeld
Met geschikte aria-live
Wijzigingen worden aangekondigd volgens hun prioriteit
<!-- Bon : avec aria-live="polite" pour informations -->
<div
class="status-banner"
aria-live="polite"
aria-atomic="true"
role="status"
>
<p>Information : Données mises à jour</p>
</div>
<!-- Bon : avec aria-live="assertive" pour alertes -->
<div
class="alert-banner"
aria-live="assertive"
aria-atomic="true"
role="alert"
>
<p>Attention : Action requise immédiatement</p>
</div>Beleefde zone (informatie)
Assertieve zone (waarschuwingen)
Voordelen:
- Belangrijke berichten onderbreken met aria-live="assertive"
- Informatieve berichten wachten met aria-live="polite"
- aria-atomic="true" kondigt volledige regio-inhoud aan
- Geschikte rollen (status, alert) bieden meer context
Uitleg: Aria-live regio's maken het mogelijk inhoudswijzigingen aan te kondigen aan schermlezers. 'polite' wacht tot de huidige aankondiging is voltooid, 'assertive' onderbreekt onmiddellijk.
Toegankelijke icoonknoppen
Slecht voorbeeld
Goed voorbeeld
Uitleg: Knoppen die alleen iconen bevatten moeten een aria-label of visueel verborgen tekst hebben om begrepen te worden door schermlezers.
Combobox / Zoeken met suggesties
Slecht voorbeeld
Zonder ARIA of toetsenbordnavigatie
Goed voorbeeld
Met ARIA en toetsenbordnavigatie
Uitleg: Een zoekopdracht met suggesties (combobox) moet toetsenbordnavigeerbaar zijn, resultaten aankondigen en selectie mogelijk maken.
Toast-meldingen (Live Regions)
Slecht voorbeeld
Zonder aria-live
❌ Meldingen worden niet aangekondigd aan schermlezers
<!-- Slecht: niet aangekondigd aan schermlezers -->
<div v-if="showToast" class="toast">
Votre demande a été enregistrée
</div>Goed voorbeeld
Met geschikte aria-live
✅ Meldingen worden correct aangekondigd
<!-- Goed: info melding (beleefd) -->
<div
v-if="showToast"
role="status"
aria-live="polite"
aria-atomic="true"
class="toast"
>
<p>Votre demande a été enregistrée</p>
<button @click="dismiss" aria-label="Fermer notification">✕</button>
</div>
<!-- Goed: foutwaarschuwing (assertief) -->
<div
v-if="showError"
role="alert"
aria-live="assertive"
aria-atomic="true"
class="toast error"
>
<p>Erreur : action impossible à exécuter</p>
<button @click="dismiss" aria-label="Fermer alerte">✕</button>
</div>Verschillen:
role="status"+aria-live="polite": Voor informatieve meldingen, onderbreekt niet het huidige lezenrole="alert"+aria-live="assertive": Voor kritieke fouten, onderbreekt onmiddellijkaria-atomic="true": Kondigt volledige meldingsinhoud aan
Uitleg: Dynamische meldingen moeten worden aangekondigd aan schermlezers zonder focus te stelen. Gebruik role='status' voor info en role='alert' voor fouten.