Schermlezers vertalen je website naar gesproken tekst of braille. Maar om dat goed te doen, hebben ze informatie nodig: wat is dit element, hoe heet het en wat is de huidige toestand? Zonder die informatie weet iemand niet op welke knop diegene klikt, wat er in een formulierveld moet, of een selectievakje aangevinkt is.
Dit is precies waarom naam, rol, waarde zo belangrijk is. Het is de taal waarmee hulptechnologieën je website begrijpen. In dit artikel leg ik uit welke informatie ze nodig hebben en hoe je die in je code zet.
Wat is WCAG 4.1.2 eigenlijk?
Succescriterium 4.1.2 Naam, rol, waarde valt onder het principe “Robuust” en stelt:
Voor alle componenten van de gebruikersinterface (inclusief, maar niet uitsluitend voor formulierelementen, links en door scripts gegenereerde componenten), kunnen de naam (name) en rol (role) door software bepaald worden; toestanden (states), eigenschappen (properties) en waarden (values) die door de gebruiker ingesteld kunnen worden, kunnen door software ingesteld worden; en kennisgeving van veranderingen in deze items is beschikbaar voor user agents, met inbegrip van hulptechnologieën.
Simpel gezegd: alle bedieningselementen op je website (zoals links, knoppen en formuliervelden) moeten in de code informatie bevatten die hulptechnologieën kunnen begrijpen. Die informatie bestaat uit vijf onderdelen:
- Naam: hoe heet het? (Bijv “Zoeken”, “E-mailadres” of “Menu”.)
- Rol: wat is het? (Bijv link, knop, invoerveld of keuzelijst.)
- Toestand: hoe staat het erbij? (Bijv aangevinkt of uitgevouwen.)
- Eigenschap: welke kenmerken heeft het? (Bijv verplicht of alleen-lezen.)
- Waarde: wat is de huidige inhoud? (Bijv “Nederland”, “70%” of de ingevoerde tekst.)
Verandert een van deze vijf, dan moet die wijziging ook worden doorgegeven. Een knop die uitklapt en dat niet meldt, valt dus ook onder dit succescriterium.
De naam van dit succescriterium is “naam, rol, waarde”. In de meeste documentatie is “waarde” een verzamelnaam voor toestanden, eigenschappen én waarden. In dit artikel splits ik ze op in drie aparte begrippen omdat het wezenlijk andere dingen zijn.
Waar die informatie terechtkomt
Een schermlezer leest geen HTML. De browser maakt uit je HTML een tweede structuur: de toegankelijkheidsboom (accessibility tree). Daarin staan per element precies die vijf onderdelen. Hulptechnologie leest alleen die boom.
Standaard HTML-elementen vullen die onderdelen zelf. Een <button> krijgt de rol “knop”, een aangevinkt selectievakje krijgt de toestand “aangevinkt”, en een <input> geeft zijn waarde door. Je hoeft daar niets aan toe te voegen. Alleen de naam moet je zelf verzorgen, met een <label> of met linktekst.
Bouw je een component uit <div>-elementen, dan blijven die onderdelen leeg. ARIA is dan je enige gereedschap om de onderdelen alsnog te vullen.
Toestand of eigenschap?
WAI-ARIA 1.2 hanteert negen toestanden: aria-busy, aria-checked, aria-current, aria-disabled, aria-expanded, aria-hidden, aria-invalid, aria-pressed en aria-selected. Al het andere is een eigenschap.
Drie van die negen zijn globaal: aria-busy, aria-current en aria-hidden. Die mag je op elk element gebruiken (ongeacht de rol). Datzelfde geldt voor de eigenschappen die over namen en beschrijvingen gaan: aria-label, aria-labelledby, aria-describedby, aria-details, aria-controls, aria-keyshortcuts en aria-roledescription.
In dit artikel noem ik per component alleen wat specifiek bij die rol hoort. De globale set komt daar altijd bij.
Naam, rol, toestand, eigenschap en waarde per component
Links
Naam van links
De toegankelijke naam van een link is wat hulptechnologie doorgeeft. Een schermlezer leest hem voor. Iemand die spraakbesturing gebruikt, spreekt hem uit om de link te activeren.
De naam van een link is standaard de content tussen de begintag en de eindtag van het <a>-element. Doorgaans is dit ook de zichtbare linktekst.
<a href="contact.html">
Neem contact met ons op
</a>
Gebruik je een afbeelding als link? Dan vormt het tekstalternatief de linktekst. Beschrijf dan waar de link naartoe gaat, niet wat er op de afbeelding te zien is.
<a href="home.html">
<img src="home-icon.png" alt="Home">
</a>
Je kunt de naam ook met aria-label of aria-labelledby instellen. Die krijgen voorrang op de linktekst bij het berekenen van de toegankelijke naam. Gebruik dat alleen als er geen bruikbare zichtbare tekst is.
Heeft een link zichtbare tekst? Dan moet die tekst ook in de toegankelijke naam staan. Als je er met aria-label een andere naam overheen zet, dan werkt spraakbesturing niet meer. Dit is een eis van succescriterium 2.5.3 Label in naam.
Rol van links
Gebruik je een <a>-element met een href-attribuut? Dan is de rol “link” automatisch ingesteld.
Zonder href is een <a>-element geen link. Een schermlezer kondigt het niet aan als link en de gebruiker kan er niet met Tab naartoe. (Dat gaat vaak mis bij links die met JavaScript worden afgehandeld.)
Navigeert het element niet naar een andere locatie, maar voert het een actie uit? Gebruik dan een <button>.
Toestanden van links
Links hebben twee eigen toestanden:
aria-expanded: geeft aan of het bijbehorende onderdeel is uitgevouwen of samengevouwen.aria-disabled: geeft aan dat de link uitgeschakeld is. (Dit is een ongebruikelijk patroon!)
Daarnaast mag je de drie globale toestanden gebruiken. Bij links is aria-current de belangrijkste. Daarmee markeer je de huidige pagina, stap of locatie. De waarden zijn page, step, location, date, time of true.
<nav aria-label="Hoofdnavigatie">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/producten" aria-current="page">Producten</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
Eigenschappen van links
Links hebben één eigen eigenschap: aria-haspopup. Die geeft aan dat de link een popup opent, zoals een menu of dialoogvenster. Van de globale eigenschappen gebruik je bij links vooral aria-describedby en aria-details.
Het attribuut aria-controls wordt alleen door JAWS ondersteund. Het ontbreken ervan is geen fout op 4.1.2.
Ook aria-invalid en aria-errormessage kun je overslaan: die zijn op de rollen link en button inmiddels verouderd. Ze horen bij formulierelementen.
Waarde van links
Links hebben geen waarde.
Knoppen
Naam van knoppen
De naam van een knop is standaard de content tussen de begintag en de eindtag van het <button>-element:
<button type="submit">
Verstuur formulier
</button>
Staat er alleen een afbeelding in de knop? Dan vormt het tekstalternatief de naam. Beschrijf het resultaat van de actie.
<button type="submit">
<img src="send-icon.png" alt="Verstuur">
</button>
Rol van knoppen
Een <button>-element krijgt de rol “knop” automatisch. Dat geldt ook voor <input type="button">, <input type="submit"> en <input type="reset">.
Het verschil met een link zit in wat er gebeurt. Navigeert het naar een andere pagina of een andere plek op dezelfde pagina? Dan is het een link. Voert het een actie uit zonder te navigeren? Dan is het een knop.
Zet type="button" op elke knop die geen formulier verstuurt. Zonder type is een knop binnen een <form>-element een verzendknop. Je uitklapknop verstuurt dan het formulier bij de eerste klik.
Toestanden van knoppen
Knoppen hebben drie eigen toestanden:
aria-pressed: geeft aan of een schakelknop is ingedrukt (true) of niet (false).aria-expanded: geeft aan of het bijbehorende onderdeel is uitgevouwen (true) of samengevouwen (false).aria-disabled: markeert een knop als uitgeschakeld, maar de knop blijft focusbaar.
Daarnaast mag je de drie globale toestanden gebruiken: aria-current, aria-busy en aria-hidden.
Een knop die iets uitvouwt, ziet er zo uit. Het hidden-attribuut verbergt het paneel en aria-expanded vertelt of het open staat:
<button type="button" aria-expanded="false" aria-controls="hoofdmenu">
Menu
</button>
<div id="hoofdmenu" hidden>
<!-- Menu-inhoud -->
</div>
Bij een schakelknop draagt aria-pressed de toestand en beschrijft het label de actie:
<button type="button" aria-pressed="true">
<span aria-hidden="true">🔈</span>
Geluid dempen
</button>
Een schermlezer kondigt dit aan als “Geluid dempen, knop, ingedrukt”. Het label zegt wat de knop doet, de toestand zegt of het aan staat.
Houd het label daarom gelijk als de toestand verandert. Verander je allebei, dan hoort de gebruiker bijvoorbeeld “Geluid uit, knop, ingedrukt” en is niet meer op te maken of het geluid nu aan of uit staat. Je kunt ook het omgekeerde doen: laat aria-pressed weg en verander alleen het label. Kies er één, nooit allebei.
Gaat het om een aan-uitschakelaar? Dan is role="switch" met aria-checked vaak logischer. De gebruiker hoort dan “aan” of “uit” in plaats van “ingedrukt”.
Voor het uitschakelen van een knop heb je twee opties. Een knop met disabled staat niet meer in de tabvolgorde. Wie met het toetsenbord navigeert komt er niet langs en ontdekt niet dat de knop bestaat. Met aria-disabled="true" blijft de knop focusbaar en hoort de gebruiker dat de actie nu niet kan. Je moet die actie dan zelf blokkeren in JavaScript.
<!-- Uitgeschakeld en uit de tabvolgorde -->
<button type="submit" disabled>
Verstuur formulier
</button>
<!-- Uitgeschakeld maar wel te vinden -->
<button type="submit" aria-disabled="true">
Verstuur formulier
</button>
Eigenschappen van knoppen
Ook knoppen hebben één eigen eigenschap: aria-haspopup. Daarnaast gebruik je de globale eigenschappen, zoals aria-describedby en aria-controls.
Gebruik aria-haspopup="menu" alleen als er echt een role="menu" achter zit, met bediening via de pijltoetsen. Bij een uitklapmenu met gewone links is dat niet zo. De schermlezer kondigt dan een menu aan dat de gebruiker vervolgens niet als menu kan bedienen.
<!-- Echt menu: aria-haspopup klopt -->
<button type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="acties">
Acties
</button>
<div role="menu" id="acties" hidden>
<div role="menuitem">Bewerken</div>
<div role="menuitem">Verwijderen</div>
</div>
<!-- Gewoon uitklapmenu: laat aria-haspopup weg -->
<button type="button" aria-expanded="false" aria-controls="accountopties">
Accountopties
</button>
<ul id="accountopties" hidden>
<li><a href="/profiel">Profiel</a></li>
<li><a href="/uitloggen">Uitloggen</a></li>
</ul>
Waarde van knoppen
Knoppen hebben geen waarde.
Formulierelementen
Naam van formulierelementen
De naam van een formulierelement komt van het bijbehorende <label>-element. Koppel dat label aan het element met het for-attribuut, dat verwijst naar het id van het invoerveld.
<label for="gebruikersnaam">
Gebruikersnaam
</label>
<input type="text" id="gebruikersnaam" name="gebruikersnaam">
Een placeholder is geen label. Die tekst verdwijnt zodra iemand begint te typen, en veel browsers geven hem niet door als naam. Gebruik altijd een echt <label>.
Horen meerdere velden bij elkaar, zoals een groep keuzerondjes? Dan heeft de groep zelf ook een naam nodig. Gebruik <fieldset> met <legend>:
<fieldset>
<legend>Hoe wil je de bestelling ontvangen?</legend>
<input type="radio" id="bezorgen" name="levering" value="bezorgen">
<label for="bezorgen">Bezorgen</label>
<input type="radio" id="afhalen" name="levering" value="afhalen">
<label for="afhalen">Afhalen</label>
</fieldset>
Rol van formulierelementen
Formulierelementen hebben verschillende standaardrollen, afhankelijk van het type:
<input type="text">: invoerveld (textbox). Ookemail,telenurlkrijgen deze rol.<input type="search">: zoekveld (searchbox).<input type="checkbox">: selectievakje (checkbox).<input type="radio">: keuzerondje (radio).<input type="range">: schuifregelaar (slider).<input type="number">: getalvak (spinbutton).<select>: keuzelijst (combobox, of listbox bijmultipleofsize).<textarea>: tekstvak (textbox met meerdere regels).
Sommige types hebben geen ARIA-rol, zoals password, date, color en file. De browser geeft ze wel correct door aan hulptechnologie, maar je vindt ze niet als rol terug in de toegankelijkheidsboom.
Toestanden van formulierelementen
Bij native velden zet je de toestand met een HTML-attribuut: checked voor een aangevinkt selectievakje of keuzerondje, selected voor de gekozen optie in een keuzelijst, en disabled voor een uitgeschakeld veld.
Bouw je het veld zelf, dan gebruik je de ARIA-varianten aria-checked, aria-selected en aria-disabled. Daar komen twee toestanden bij die geen HTML-tegenhanger hebben:
aria-expanded: geeft aan of de bijbehorende lijst open staat. Verplicht oprole="combobox".aria-invalid: geeft aan dat de invoer een fout bevat.
<!-- Aangevinkt selectievakje -->
<input type="checkbox" id="nieuwsbrief" checked>
<label for="nieuwsbrief">
Ontvang nieuwsbrief
</label>
<!-- Invoerveld met validatiefout -->
<label for="email-validatie">
E-mailadres
</label>
<input type="email"
id="email-validatie"
aria-invalid="true"
aria-describedby="email-fout">
<p id="email-fout">
Vul een geldig e-mailadres in.
</p>
De attributen checked, selected en value in je HTML geven de beginwaarde, niet de huidige toestand. Vinkt iemand een selectievakje aan, dan werkt de browser de eigenschap in het DOM bij en niet het attribuut in de code. De toegankelijkheidsboom klopt wel. Controleer dus altijd de boom of het live DOM, nooit “view source”.
Eigenschappen van formulierelementen
Ook hier geldt de tweedeling. Bij native velden gebruik je required voor een verplicht veld en readonly voor een veld dat alleen te lezen is. Bij maatwerk gebruik je aria-required en aria-readonly.
Het verschil tussen disabled en readonly is belangrijk. Een veld met disabled is niet bereikbaar met het toetsenbord en de waarde gaat niet mee bij het versturen. Een veld met readonly is wel bereikbaar en de waarde gaat wel mee. Gebruik readonly als de gebruiker de waarde moet kunnen lezen of kopiëren.
Voor invoervelden zijn deze eigenschappen daarnaast nuttig:
autocomplete: geeft aan welk soort gegeven je verwacht, zodat de browser kan invullen. HTML-attribuut.aria-describedby: verwijst naar uitleg of instructies bij het veld.aria-errormessage: verwijst naar de foutmelding. Werkt samen metaria-invalid="true".aria-autocomplete: beschrijft welke suggesties het veld geeft (none,inline,listofboth). Dit is iets anders dan het HTML-attribuutautocomplete.aria-activedescendant: verwijst naar het gemarkeerde item in een keuzelijst, terwijl de focus op het invoerveld blijft.aria-multilineenaria-placeholder: alleen voor maatwerk tekstvelden. Bij een<textarea>regelt de browser dit.
<!-- Verplicht veld -->
<label for="email">
E-mailadres
</label>
<input type="email" id="email" required>
<!-- Alleen-lezen invoerveld -->
<label for="klantnummer">
Klantnummer
</label>
<input type="text" id="klantnummer" value="NL-4821" readonly>
<!-- Met autocomplete -->
<label for="postcode">
Postcode
</label>
<input type="text" id="postcode" autocomplete="postal-code">
<!-- Met hulptekst -->
<label for="wachtwoord">
Wachtwoord
</label>
<input type="password" id="wachtwoord" aria-describedby="wachtwoord-eisen">
<p id="wachtwoord-eisen">
Minimaal 8 tekens met letters en cijfers
</p>
Gebruik bij native formulierelementen altijd de HTML-attributen (required, disabled, readonly) en niet de ARIA-varianten. HTML werkt betrouwbaarder en regelt ook het gedrag. De ARIA-varianten zijn er eigenlijk alleen voor componenten die je zelf bouwt.
Waarde van formulierelementen
De waarde is wat de gebruiker heeft ingevoerd of geselecteerd. Bij een native formulierelement geeft de browser die waarde vanzelf door aan hulptechnologie. Bij een schuifregelaar of getalvak geven min, max en value ook het bereik door.
<!-- Keuzelijst met geselecteerde optie -->
<label for="land">
Land
</label>
<select id="land">
<option>Kies een land</option>
<option selected>Nederland</option>
<option>België</option>
</select>
<!-- Schuifregelaar met bereik -->
<label for="volume">
Volume
</label>
<input type="range" id="volume" min="0" max="100" value="70">
Maatwerk componenten
Tot nu toe ging het over standaard HTML-elementen. Die begrijpen browsers en hulptechnologieën automatisch. Bouw je zelf een tabblad, accordeon of carrousel uit <div>-elementen, dan is de toegankelijkheidsboom leeg en moet je alles zelf vullen.
Onthoud daarbij de eerste regel van ARIA: gebruik geen ARIA als je een native HTML-element kunt gebruiken.
Voor naam
Heeft je component zichtbare tekst die als naam kan dienen? Verwijs daar dan naar met aria-labelledby. Is er geen zichtbare tekst, gebruik dan aria-label.
<!-- Naam uit zichtbare tekst -->
<h2 id="dialoog-titel">
Instellingen wijzigen
</h2>
<div role="dialog" aria-labelledby="dialoog-titel">
<!-- Dialooginhoud -->
</div>
<!-- Naam zonder zichtbare tekst -->
<button type="button" aria-label="Sluiten">
<span aria-hidden="true">×</span>
</button>
Let op dat je een naam niet op elk element kwijt kunt. Op een <div>, <span>, <p> of <li> zonder rol negeren browsers aria-label en aria-labelledby. Geef het element eerst een rol, of gebruik een native element.
Voor rol
Het role-attribuut bepaalt wat het element is. Dit zijn de rollen die je bij bedieningselementen het vaakst nodig hebt.
Losse bedieningselementen:
role="button"enrole="link": voor knoppen en links.role="checkbox"enrole="radio": voor selectievakjes en keuzerondjes.role="switch": voor schuifschakelaars.role="slider"enrole="spinbutton": voor schuifregelaars en getalvakken.role="textbox"enrole="searchbox": voor tekstinvoer.
Samengestelde componenten. Deze rollen komen altijd in combinatie voor, want een onderdeel heeft de bovenliggende rol nodig om betekenis te krijgen:
role="combobox",role="listbox"enrole="option": voor keuzelijsten.role="tablist",role="tab"enrole="tabpanel": voor een tablijst.role="menu",role="menubar"enrole="menuitem": voor menu’s.role="tree"enrole="treeitem": voor boomstructuren.role="grid",role="row"enrole="gridcell": voor interactieve rasters.role="toolbar": voor een groep bedieningselementen op één rij.
Meldingen en meting:
role="dialog"enrole="alertdialog": voor dialoogvensters.role="alert",role="status",role="log"enrole="timer": voor statusberichten.role="tooltip": voor een tooltip met extra uitleg.role="progressbar": voor een voortgangsbalk.role="meter": voor een vaste meetwaarde, zoals een batterijniveau.
Een rol vult alleen de toegankelijkheidsboom. Het gedrag krijg je er niet bij. Bij role="button" op een <div> moet je zelf zorgen voor tabindex="0", voor het activeren met Enter en Spatie, en voor een zichtbare focusindicator. Dat is precies waarom je een native <button> gebruikt als het kan.
Voor toestand
Welke toestand je nodig hebt, volgt uit de rol. Een schuifschakelaar gebruikt aria-checked, een tab gebruikt aria-selected, een accordeonknop gebruikt aria-expanded. Sommige zijn verplicht: zonder aria-checked zegt role="switch" nog niets over de stand.
Let op het verschil tussen aankruisen en selecteren. Een selectievakje gebruikt aria-checked, een optie in een keuzelijst gebruikt aria-selected. Deze zijn niet uitwisselbaar.
<!-- Maatwerk selectievakje -->
<div role="checkbox" aria-checked="true" tabindex="0">
Akkoord met de voorwaarden
</div>
<!-- Optie in een keuzelijst -->
<div role="option" aria-selected="true">
Nederland
</div>
Zet aria-hidden="true" nooit op een element dat focusbaar is of dat focusbare elementen bevat. Het element verdwijnt dan uit de toegankelijkheidsboom, terwijl de gebruiker er nog wel met Tab naartoe kan. Die belandt dan op iets wat niet wordt aangekondigd. Wil je iets echt verbergen? Gebruik hidden of display: none.
Voor eigenschap
Eigenschappen geven extra context over een element.
Vereisten en beperkingen:
aria-required: markeert een maatwerkveld als verplicht.aria-readonly: markeert een maatwerkveld als alleen-lezen.aria-multiline: geeft aan dat een tekstveld meerdere regels accepteert.aria-multiselectable: geeft aan dat je meerdere items kunt selecteren.
Positie en structuur:
aria-posinsetenaria-setsize: geven de positie in een set en het totale aantal items aan.aria-level: geeft het niveau aan in een hiërarchie, zoals een kop of een boomstructuur.aria-sort: geeft de sorteerrichting van een tabelkop aan.aria-orientation: beschrijft de oriëntatie van een element.
Popup en interactie:
aria-haspopup: geeft aan dat een element een popup opent.aria-controls: verwijst naar het element dat wordt bestuurd.aria-owns: legt een ouder-kindrelatie vast tussen elementen die in de code niet genest zijn.aria-activedescendant: verwijst naar het actieve item in een samengesteld component.aria-autocomplete: beschrijft het soort autocomplete.aria-modal: geeft aan of een dialoogvenster modaal is.
Beschrijvingen en relaties:
aria-describedby: verwijst naar een element met uitleg of instructies.aria-details: verwijst naar een element met gedetailleerde informatie.aria-errormessage: verwijst naar een foutmelding, samen metaria-invalid="true".aria-keyshortcuts: beschrijft sneltoetsen.aria-roledescription: geeft een aangepaste beschrijving van de rol.
Live regions voor dynamische updates:
aria-live: geeft de prioriteit van de aankondiging aan (off,politeofassertive).aria-atomic: geeft aan of de hele regio of alleen de wijziging wordt aangekondigd.aria-relevant: geeft aan welke wijzigingen worden aangekondigd.
<!-- Verplicht maatwerkveld -->
<span id="email-label">
E-mailadres
</span>
<div role="textbox"
contenteditable="true"
aria-labelledby="email-label"
aria-required="true">
</div>
<!-- Positie in een set die stapsgewijs laadt -->
<ul role="listbox" aria-label="Zoekresultaten">
<li role="option" aria-posinset="2" aria-setsize="50">
Amsterdam
</li>
</ul>
<!-- Sorteerbare kolomkop -->
<th aria-sort="ascending">
<button type="button">Naam</button>
</th>
<!-- Live regio voor updates -->
<div aria-live="polite" aria-atomic="true">
3 nieuwe berichten ontvangen
</div>
Voor waarde
Schuifregelaars, getalvakken en voortgangsbalken hebben een numerieke waarde. Geef altijd alle drie de getallen door: aria-valuenow, aria-valuemin en aria-valuemax. Zonder het bereik weet de gebruiker niet wat “45” betekent.
Zegt het getal alleen niets? Voeg dan aria-valuetext toe met een leesbare versie.
<!-- Maatwerk schuifregelaar -->
<div role="slider"
aria-label="Volume"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="70"
tabindex="0"></div>
<!-- Met een leesbare waarde -->
<div role="progressbar"
aria-label="Bestelling verwerken"
aria-valuemin="0"
aria-valuemax="4"
aria-valuenow="2"
aria-valuetext="Stap 2 van 4: betaling controleren"></div>
<!-- Voortgang zonder bekende eindwaarde: laat de getallen weg -->
<div role="progressbar" aria-label="Gegevens laden"></div>
Werk toestanden bij met JavaScript
Een toestand in je HTML zetten is niet genoeg. Verandert er iets, dan moet de toegankelijkheidsboom mee veranderen. Bij native elementen doet de browser dat. Bij maatwerk doe je het zelf.
Voorbeeld van een uitvouwbare sectie:
<button type="button" aria-expanded="false" aria-controls="extra-info">
Meer informatie
</button>
<div id="extra-info" hidden>
<p>Extra inhoud die je kunt tonen of verbergen.</p>
</div>
<script>
document.querySelector('[aria-controls="extra-info"]')
.addEventListener('click', function () {
const isUitgevouwen = this.getAttribute('aria-expanded') === 'true';
const paneel = document.getElementById(this.getAttribute('aria-controls'));
this.setAttribute('aria-expanded', String(!isUitgevouwen));
paneel.hidden = isUitgevouwen;
});
</script>
De knop begint samengevouwen. Bij een klik leest het script de huidige waarde, draait die om en verbergt of toont het paneel. Vergeet je die update, dan blijft de schermlezer melden dat de sectie dicht is terwijl de inhoud zichtbaar is. Dat is de meest voorkomende fout bij maatwerk componenten.
Voorbeelden
Tablijst
<div role="tablist" aria-label="Productinformatie">
<button type="button"
role="tab"
id="tab-specificaties"
aria-selected="true"
aria-controls="paneel-specificaties">
Specificaties
</button>
<button type="button"
role="tab"
id="tab-reviews"
aria-selected="false"
aria-controls="paneel-reviews"
tabindex="-1">
Reviews
</button>
</div>
<div id="paneel-specificaties" role="tabpanel" aria-labelledby="tab-specificaties">
<!-- Inhoud specificaties -->
</div>
<div id="paneel-reviews" role="tabpanel" aria-labelledby="tab-reviews" hidden>
<!-- Inhoud reviews -->
</div>
- Naam: de tekst in de tabknoppen, en
aria-labelledbyvoor de panelen. - Rol:
role="tablist",role="tab"enrole="tabpanel". - Toestand:
aria-selecteden hethidden-attribuut. - Eigenschap:
aria-controlskoppelt de tab aan het paneel.
De tabindex="-1" op de inactieve tab hoort bij de toetsenbordbediening: je tabt één keer de tablijst in en wisselt daarna met de pijltoetsen. Dat gedrag bouw je zelf, net als het bijwerken van aria-selected en hidden bij elke wissel.
Accordeon
<h3>
<button type="button"
id="sectie-faq-kop"
aria-expanded="false"
aria-controls="sectie-faq-inhoud">
Veelgestelde vragen
</button>
</h3>
<div id="sectie-faq-inhoud"
role="region"
aria-labelledby="sectie-faq-kop"
hidden>
<!-- Accordeon-inhoud -->
</div>
- Naam: de knoptekst, en
aria-labelledbyvoor de inhoud. - Rol: de standaardrol van de knop, en
role="region"voor de inhoud. - Toestand:
aria-expandeden hethidden-attribuut. - Eigenschap:
aria-controlskoppelt de knop aan de inhoud.
De knop staat in de <h3>, niet andersom. Zo blijft de kop een kop en de knop een knop. Zet je de kop in de knop, dan verliest die zijn betekenis: alles binnen een knop wordt platgeslagen tot tekst en verdwijnt uit de koppenlijst. Om die reden zet je ook nooit een link of een tweede knop in een knop.
Hoe test je dit?
- Bekijk de toegankelijkheidsboom: Gebruik de toegankelijkheidsinspector in Firefox of het accessibility-paneel in Chrome. Daar zie je de naam, de rol en de toestanden zoals hulptechnologie ze krijgt.
- Bedien het component en kijk opnieuw: Vouw uit, vink aan, versleep. Verandert de toestand mee? Zo niet, dan mist er een update.
- Navigeer met het toetsenbord: Kom je bij elk bedieningselement met Tab, en kun je het activeren met Enter of Spatie? Bij maatwerk moet je dat zelf hebben gebouwd.
- Test met een schermlezer: NVDA op Windows of VoiceOver op macOS. Luister of de naam, rol en toestand kloppen met wat je ziet.
- Draai een automatische controle: axe DevTools vindt ontbrekende namen, ongeldige rollen en verkeerde ARIA-combinaties. Dat is alvast een deel van het werk.
Kijk niet in de code van de pagina. Die toont de beginsituatie, niet de huidige toestand. Lees meer over hulpmiddelen bij het toetsen.
Vereisten en aanbevelingen voor naam, rol en waarde
Dit zijn de vereisten en aanbevelingen voor bedieningselementen die hulptechnologie kan begrijpen.
WCAG-vereisten
- Elk bedieningselement heeft een toegankelijke naam die het doel beschrijft.
- Elk bedieningselement heeft een rol die past bij wat het doet.
- Toestanden die de gebruiker zelf instelt, staan in de code. Denk aan aangevinkt, uitgevouwen of ingedrukt.
- Verandert een toestand of waarde, dan wordt die wijziging doorgegeven aan hulptechnologie.
- Zichtbare tekst op een bedieningselement staat ook in de toegankelijke naam.
Aanbevolen
- Gebruik native HTML-elementen. Dan krijg je rol en toestand er alvast bij.
- Markeer de huidige pagina in een navigatie met
aria-current="page".
Conclusie
Naam, rol, waarde gaat niet over ARIA toevoegen. Het gaat erover dat de toegankelijkheidsboom klopt.
Gebruik standaard HTML-elementen waar mogelijk en ARIA alleen waar nodig.
Gerelateerde succescriteria
Dit artikel behandelt het WCAG-succescriterium 4.1.2 Naam, rol, waarde (niveau A) en raakt ook aan 2.5.3 Label in naam (niveau A) en 3.3.1 Foutidentificatie (niveau A).