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 een gebruiker niet:
- Op welke knop hij gaat klikken?
- Wat hij in een formulierveld moet invullen?
- Of een selectievakje aangevinkt is of niet?
Dit is precies waarom naam, rol, waarde zo belangrijk is. Het is de taal waarmee hulptechnologieën je website begrijpen.
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, formuliervelden, enz.) moeten in de code informatie bevatten die hulptechnologieën goed kunnen begrijpen. Deze informatie bestaat uit:
- Naam: Hoe heet het? (bijv. “Zoeken”, “E-mailadres”, “Menu”)
- Rol: Wat is het? (bijv. link, knop, invoerveld, keuzelijst)
- Toestand: Hoe staat het erbij? (bijv. aangevinkt, uitgevouwen, ongeldig)
- Eigenschap: Welke kenmerken heeft het? (bijv. verplicht, alleen-lezen)
- Waarde: Wat is de huidige inhoud? (bijv. “Nederland”, “70%”, de ingevoerde tekst)
Dit succescriterium is vooral bedoeld voor ontwikkelaars die hun eigen componenten bouwen. Standaard HTML-elementen (zoals <a>, <button> en <input>) voldoen automatisch aan dit criterium als je ze volgens specificatie gebruikt.
Let op: 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 deze op in drie aparte begrippen omdat het wezenlijk andere dingen zijn. Een toestand verandert door interactie (zoals aria-expanded), een eigenschap staat vast (zoals aria-required), en een waarde geeft de huidige inhoud weer (zoals aria-valuenow).
Naam, rol, toestand, eigenschap en waarde voor verschillende componenten
Laten we eens kijken naar hoe je naam, rol, toestand, eigenschap en waarde toepast bij de meest voorkomende componenten.
Links
Naam van links
De naam van een link is wat een schermlezer presenteert om de gebruiker te vertellen waar de link naar verwijst. Standaard is dit alle 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.
<a href="home.html">
<img src="home-icon.png" alt="Home">
</a>
Rol van links
Als je een <a>-element gebruikt in combinatie met het href-attribuut, dan is de rol “link” automatisch ingesteld.
Toestand van links
Links kunnen verschillende toestanden hebben:
aria-current: Geeft aan dat dit de huidige pagina, stap of locatie isaria-expanded: Geeft aan dat de link kan uitvouwen en weer samenvouwenaria-disabled: Geeft aan dat de link uitgeschakeld is, verwijder dan ook dehref(dit is een erg ongebruikelijk patroon)
<!-- Huidige pagina in navigatie -->
<nav>
<a href="/">Home</a>
<a href="/producten" aria-current="page">Producten</a>
<a href="/contact">Contact</a>
</nav>
<!-- Link die sectie uitklapt -->
<a href="#details" aria-expanded="false" aria-controls="details">
Meer informatie
</a>
<div id="details" hidden>
Hier staat extra informatie.
</div>
Eigenschap van links
Links kunnen eigenschappen hebben die extra context geven:
aria-describedby: Verwijst naar een element met extra beschrijvingaria-haspopup: Geeft aan of een link is uitgevouwen of samengevouwenaria-controls: Verwijst naar een element dat de link bestuurt
<!-- Link met extra beschrijving -->
<a href="document.pdf" aria-describedby="pdf-info">
Jaarverslag 2024
</a>
<span id="pdf-info" hidden>Opent PDF, 2.4 MB</span>
<!-- Link met popup -->
<a href="#menu" aria-haspopup="menu" aria-expanded="false">
Account-opties
</a>
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>
Voor knoppen met alleen een afbeelding vormt het tekstalternatief de toegankelijke naam:
<button type="submit">
<img src="send-icon.png" alt="Verstuur">
</button>
Rol van knoppen
Knoppen hebben standaard de rol “button”. Als je een <button>-element gebruikt, dan is deze rol automatisch ingesteld.
Toestand van knoppen
Knoppen kunnen verschillende toestanden hebben die door de gebruiker of door de interface worden gewijzigd:
aria-pressed: Geeft aan of een knop is ingedrukt of nietaria-expanded: Geeft aan of een knop is uitgevouwen of samengevouwendisabled: Markeert een knop als uitgeschakeld (HTML-attribuut)aria-disabled: Alternatief voordisabled(maar de knop blijft focusbaar)
<!-- Schakelknop -->
<button type="button" aria-pressed="true">
<span aria-hidden="true">🔇</span>
Geluid uit
</button>
<!-- Uitklapknop -->
<button aria-expanded="false" aria-controls="menu">
Menu
</button>
<div id="menu" hidden>
<!-- Menu-inhoud -->
</div>
<!-- Uitgeschakelde knop -->
<button type="submit" disabled>
Verstuur formulier
</button>
Het aria-pressed-attribuut geeft aan of een schakelknop ingedrukt (true) of niet ingedrukt (false) is. Deze toestand verandert wanneer de gebruiker op de knop klikt.
Eigenschap van knoppen
Knoppen kunnen verschillende eigenschappen hebben:
aria-describedby: Verwijst naar een element met een extra beschrijvingaria-haspopup: Geeft aan dat de knop een popup, menu of dialoogvenster opentaria-controls: Verwijst naar een element dat de knop bestuurt
<!-- Knop die menu opent -->
<button aria-haspopup="menu" aria-expanded="false" aria-controls="acties">
Acties
</button>
Een uitgeschakelde knop kan de gebruiker niet bereiken met het toetsenbord en geeft aan dat de actie op dit moment niet beschikbaar is.
Formulierelementen
Naam van formulierelementen
De naam van een formulierelement komt van het bijbehorende <label>-element. Koppel dit 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">
Rol van formulierelementen
Formulierelementen hebben verschillende standaardrollen, afhankelijk van het type:
<input type="text">: invoerveld (textbox)<input type="email">: e-mailinvoerveld (textbox)<input type="checkbox">: selectievakje (checkbox)<input type="radio">: keuzerondje (radiobutton)<input type="range">: schuifregelaar (slider)<input type="number">: numeriek invoerveld (spinbutton)<select>: keuzelijst (combobox of listbox)<textarea>: tekstvak (textbox met multiline)
Zorg dat je het juiste type gebruikt voor het doel van het formulierelement.
Toestand van formulierelementen
Formulierelementen hebben toestanden die veranderen door gebruikersinteractie of validatie:
checked: Voor aangevinkte selectievakjes en keuzerondjes (HTML-attribuut)aria-invalid: Geeft aan dat een invoerveld een fout bevataria-checked: Voor aangepaste selectievakjes
<!-- 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>
Let op: Gebruik aria-checked alleen voor aangepaste selectievakjes die je zelf bouwt met <div> of <span>.
Eigenschap van formulierelementen
Formulierelementen kunnen verschillende eigenschappen hebben die extra context geven:
required: Markeert een veld als verplicht (HTML-attribuut)disabled: Markeert een veld als uitgeschakeld (HTML-attribuut)readonly: Markeert een veld als alleen-lezen (HTML-attribuut)aria-required: Alternatieve manier om veld als verplicht te markeren (bij maatwerk)aria-describedby: Verwijst naar een element met een extra uitleg of instructiesautocomplete: Geeft aan welk type informatie wordt verwachtaria-errormessage: Verwijst naar een element met een foutmelding
<!-- Verplicht veld -->
<label for="email">
E-mailadres
</label>
<input type="email" id="email" required>
<!-- Uitgeschakeld invoerveld -->
<input type="text" value="Niet bewerkbaar" disabled>
<!-- Alleen-lezen invoerveld -->
<input type="text" value="Alleen bekijken" 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>
Het verschil tussen disabled en readonly is belangrijk: de gebruiker kan een disabled veld niet bereiken met het toetsenbord. Een readonly veld wel. Gebruik readonly als de gebruiker de waarde moet kunnen kopiëren of als de waarde mee moet worden verstuurd bij het verzenden van het formulier.
Let op bij HTML-attributen: Gebruik altijd de HTML-attributen (required, disabled, readonly) in plaats van de ARIA-equivalenten (aria-required, aria-disabled, aria-readonly) voor native formulierelementen. HTML heeft voorrang en werkt betrouwbaarder.
Waarde van formulierelementen
De waarde van een formulierelement is wat de gebruiker heeft ingevoerd of geselecteerd:
- Ingevoerde tekst in invoervelden en tekstvakken
- Geselecteerde opties in keuzelijsten
- Status van selectievakjes en keuzerondjes
<!-- Invoerveld met waarde -->
<label for="naam">
Naam
</label>
<input type="text" id="naam" value="Jan Jansen">
<!-- 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>
Maatwerk componenten
Tot nu toe hebben we standaard HTML-elementen besproken. De browser en hulptechnologieën begrijpen deze elementen automatisch. Maar wat als je eigen componenten bouwt, zoals tabbladen, accordeons of carrousels? Dan moet je zelf de juiste informatie toevoegen.
Hiervoor gebruik je ARIA (Accessible Rich Internet Applications). Maar onthoud altijd de eerste regel van ARIA: Gebruik geen ARIA als je native HTML-elementen kunt gebruiken.
Hier zijn de belangrijkste ARIA-attributen voor naam, rol en waarde:
Voor naam
ARIA biedt verschillende manieren om een toegankelijke naam te geven:
aria-labelledby: Verwijst naar hetidvan één of meer elementen die samen de naam vormenaria-label: Geeft direct een naam, bijvoorbeeld als er geen zichtbare tekst is
<!-- aria-labelledby -->
<h2 id="dialogTitle">
Instellingen wijzigen
</h2>
<div role="dialog" aria-labelledby="dialogTitle">
<!-- Dialooginhoud -->
</div>
<!-- aria-label -->
<button aria-label="Sluiten">
X
</button>
Lees meer over hoe browsers de toegankelijke naam berekenen.
Voor rol
Het role-attribuut bepaalt de functie van een element. Veelgebruikte rollen zijn:
Interactieve elementen:
role="button": Voor aangepaste knoppenrole="link": Voor aangepaste linksrole="checkbox": Voor aangepaste selectievakjesrole="radio": Voor aangepaste keuzerondjesrole="switch": Voor schuifschakelaarsrole="slider": Voor schuifregelaarsrole="spinbutton": Voor numerieke invoer met +/- knoppenrole="textbox": Voor aangepaste tekstinvoerrole="searchbox": Voor zoekveldenrole="combobox": Voor keuzelijsten met autocomplete
Structuur en navigatie:
role="banner": Voor de headerrole="navigation": Voor navigatiegebiedenrole="search": Voor zoekgebiedenrole="main": Voor hoofdinhoudrole="complementary": Voor aanvullende inhoud (sidebar)role="contentinfo": Voor de footer
Samengestelde componenten:
role="tablist",role="tab",role="tabpanel": Voor tabpanelenrole="menu",role="menubar",role="menuitem": Voor menu’srole="listbox",role="option": Voor aangepaste keuzelijstenrole="tree",role="treeitem": Voor boomstructurenrole="grid",role="row",role="cell": Voor interactieve rasters
Communicatie:
role="dialog": Voor dialoogvenstersrole="alertdialog": Voor dialoogvensters met een waarschuwingrole="alert": Voor statusberichten met een belangrijke meldingrole="status": Voor statusberichten met een statusupdaterole="log": Voor logberichtenrole="marquee": Voor scrollende contentrole="timer": Voor tijdweergave
Groepering:
role="group": Voor een groep elementenrole="region": Voor belangrijk gebied (vereist toegankelijke naam)role="article": Voor zelfstandig artikelrole="list",role="listitem": Voor aangepaste lijsten
Presentatie:
role="img": Voor complexe afbeeldingen (SVG, canvas)role="figure": Voor figuren met bijschriftrole="presentation"ofrole="none": Verbergt semantische betekenis
Voor toestand
Toestanden geven aan hoe een element op dit moment staat. Ze veranderen door gebruikersinteractie of door scripts:
Uitklapbare elementen:
aria-expanded: Geeft aan of iets uitgevouwen (true) of samengevouwen (false) is
Selectie en activering:
aria-pressed: Voor knoppen die aan/uit kunnen staan (true/false/mixed)aria-checked: Voor aangepaste selectievakjes of keuzerondjes (true/false/mixed)aria-selected: Voor geselecteerde items (true/false)aria-current: Geeft aan welk item actief is (page/step/location/date/time/true/false)
Beschikbaarheid:
aria-disabled: Markeert een element als uitgeschakeld maar het element is nog wel focusbaar (true/false)aria-hidden: Verbergt een element voor hulptechnologie (true/false)aria-busy: Geeft aan dat een element wordt bijgewerkt (true/false)
Validatie:
aria-invalid: Geeft aan dat invoer ongeldig is (true/false/grammar/spelling)
<!-- aria-expanded -->
<button aria-expanded="false" aria-controls="panel">
Meer opties
</button>
<div id="panel" hidden>
<!-- Paneel inhoud -->
</div>
<!-- aria-pressed -->
<button aria-pressed="true">
Geluid aan
</button>
<!-- aria-selected -->
<div role="option" aria-selected="true">
Nederland
</div>
<!-- aria-checked (alleen voor maatwerk checkbox!) -->
<div role="checkbox" aria-checked="true" tabindex="0">
Akkoord met voorwaarden
</div>
<!-- aria-current -->
<nav>
<a href="/" aria-current="page">
Home
</a>
<a href="/producten">
Producten
</a>
</nav>
<!-- aria-invalid -->
<input type="email" id="email" aria-invalid="true" aria-describedby="email-fout">
<p id="email-fout">
Vul een geldig e-mailadres in.
</p>
<!-- aria-busy -->
<div aria-busy="true" aria-live="polite">
Resultaten worden geladen...
</div>
Voor eigenschap
Eigenschappen geven extra context over een element die meestal niet verandert door gebruikersinteractie:
Vereisten en beperkingen:
aria-required: Geeft aan dat een veld verplicht is (true/false) – bij maatwerk componentenaria-readonly: Geeft aan dat een veld alleen-lezen is (true/false) – bij maatwerk componentenaria-multiline: Geeft aan dat een tekstveld meerdere regels accepteert (true/false)aria-multiselectable: Geeft aan dat meerdere items geselecteerd kunnen worden (true/false)
Popup en interactie:
aria-haspopup: Geeft aan dat een element een popup opent (menu/dialog/grid/listbox/tree/true/false)aria-controls: Verwijst naar hetidvan het element dat wordt bestuurdaria-owns: Definieert een ouder-kind relatie tussen elementenaria-activedescendant: Verwijst naar actieve descendant in samengesteld component
Live regions (dynamische updates):
aria-live: Geeft prioriteit van aankondiging aan (off/polite/assertive)aria-atomic: Geeft aan of een hele regio (true) of alleen een wijziging (false) aangekondigd wordenaria-relevant: Geeft aan welke wijzigingen aangekondigd worden (additions/removals/text/all)
Relaties:
aria-describedby: Verwijst naar een element met extra uitleg of instructiesaria-details: Verwijst naar een element met gedetailleerde informatiearia-errormessage: Verwijst naar een element met een foutmeldingaria-flowto: Bepaalt een alternatieve leesvolgorde
Overige:
aria-keyshortcuts: Beschrijft toetsenbordsnelkoppelingen (bijv. “Alt + S”)aria-roledescription: Geeft een aangepaste beschrijving van de rolaria-orientation: Beschrijft de oriëntatie van element (horizontal/vertical)aria-autocomplete: Beschrijft het soort autocomplete (none/inline/list/both)aria-modal: Geeft aan of een dialoogvenster modaal is (true/false)
<!-- Verplicht veld (maatwerk) -->
<label id="custom-label">
E-mailadres
</label>
<div role="textbox"
contenteditable="true"
aria-labelledby="custom-label"
aria-required="true">
</div>
<!-- Element met popup -->
<button aria-haspopup="menu" aria-expanded="false" aria-controls="opties-menu">
Opties
</button>
<div role="menu" id="opties-menu" hidden>
<div role="menuitem">Bewerken</div>
<div role="menuitem">Verwijderen</div>
</div>
<!-- Element dat ander element bestuurt -->
<button aria-expanded="false" aria-controls="menu-items">
Menu
</button>
<div id="menu-items" hidden>
<!-- Menu-items -->
</div>
<!-- Uitgeschakeld element (nog wel focusbaar) -->
<button aria-disabled="true">
Versturen niet mogelijk
</button>
<!-- Live regio voor updates -->
<div aria-live="polite" aria-atomic="true">
3 nieuwe berichten ontvangen
</div>
<!-- Live regio voor urgente updates -->
<div role="alert" aria-live="assertive">
Fout: verbinding verbroken
</div>
Voor waarde
Schuifregelaars, voortgangsbalken en spinbuttons tonen een numerieke waarde. Gebruik deze attributen om de waarde aan hulptechnologieën door te geven:
aria-valuemin– Minimumwaarde (getal)aria-valuemax– Maximumwaarde (getal)aria-valuenow– Huidige waarde (getal)aria-valuetext– Tekstuele weergave van de waarde (string)
Let op: Voor native HTML-elementen zoals <input type="range"> en <input type="number"> gebruik je de HTML-attributen min, max en value. Die hebben voorrang boven de ARIA-attributen.
<!-- Aangepaste schuifregelaar -->
<div role="slider" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="70" tabindex="0"></div>
<!-- Met aria-valuetext voor duidelijkere beschrijving -->
<div role="slider" aria-label="Temperatuur" aria-valuemin="16" aria-valuemax="30" aria-valuenow="21" aria-valuetext="21 graden Celsius" tabindex="0"></div>
<!-- Voortgangsbalk -->
<div role="progressbar" aria-label="Bestand uploaden" aria-valuemin="0" aria-valuemax="100" aria-valuenow="45"></div>
<!-- Met aria-valuetext voor stappen -->
<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>
<!-- Onbepaalde voortgang (geen bekende eindwaarde) -->
<div role="progressbar" aria-label="Gegevens laden"></div>
<!-- Native HTML range (gebruik HTML-attributen!) -->
<label for="volume">Volume</label>
<input type="range" id="volume" min="0" max="100" value="70" aria-valuetext="70 procent">
Dynamische updates met JavaScript
Bij maatwerk componenten veranderen toestanden vaak door gebruikersinteractie. Denk aan een accordeon die openklapt of een tabblad dat actief wordt. Het is niet genoeg om alleen de ARIA-attributen in je HTML te zetten. Je moet ze ook bijwerken met JavaScript wanneer de toestand verandert.
Hier is een voorbeeld van een uitvouwbare sectie:
<button aria-expanded="false" aria-controls="extra-info" onclick="togglePanel(this)">
Meer informatie
</button>
<div id="extra-info" hidden>
<p>Extra inhoud die je kunt tonen of verbergen.</p>
</div>
<script>
function togglePanel(button) {
// Huidige toestand ophalen
const isExpanded = button.getAttribute('aria-expanded') === 'true';
const panel = document.getElementById(button.getAttribute('aria-controls'));
// Toestand omdraaien
button.setAttribute('aria-expanded', !isExpanded);
panel.hidden = isExpanded;
}
</script>
Dit is wat er gebeurt:
- De knop begint met
aria-expanded="false". - Wanneer de gebruiker klikt, leest JavaScript de huidige waarde.
- JavaScript zet
aria-expandedoptrueen verwijdert hethidden-attribuut van het paneel. - De schermlezer krijgt dan de juiste toestand te horen.
Zonder deze JavaScript-update zou de schermlezer blijven melden dat de sectie samengevouwen is, ook al is de inhoud zichtbaar. Dat is verwarrend voor gebruikers.
Voorbeelden
Voorbeelden van wanneer ARIA nuttig kan zijn:
Tabbladen (voorbeeld)
<div role="tablist" aria-label="Productinformatie">
<button role="tab"
id="tab-spec"
aria-selected="true"
aria-controls="panel-spec">
Specificaties
</button>
<button role="tab"
id="tab-reviews"
aria-selected="false"
aria-controls="panel-reviews"
tabindex="-1">
Reviews
</button>
</div>
<div id="panel-spec"
role="tabpanel"
aria-labelledby="tab-spec">
<!-- Inhoud specificaties -->
</div>
<div id="panel-reviews"
role="tabpanel"
aria-labelledby="tab-reviews"
hidden>
<!-- Inhoud reviews -->
</div>
In dit voorbeeld:
- Naam: Komt van de tekst in de tabknoppen en
aria-labelledbyvoor de panelen - Rol: Expliciet aangegeven met
role="tablist",role="tab"enrole="tabpanel" - Toestand: Aangegeven met
aria-selected="true/false"en hethiddenattribuut - Eigenschap:
aria-controlsgeeft aan welk paneel bij welke tab hoort
Accordeon (voorbeeld)
<div class="accordion">
<h3>
<button aria-expanded="false"
aria-controls="section1-content"
id="section1-header">
Veelgestelde vragen
</button>
</h3>
<div id="section1-content"
role="region"
aria-labelledby="section1-header"
hidden>
<!-- Accordeon inhoud -->
</div>
</div>
In dit voorbeeld:
- Naam: De knoptekst en
aria-labelledbyvoor de inhoud - Rol: De standaard rol voor de knop en
role="region"voor de inhoud - Toestand:
aria-expanded="true/false"enhidden - Eigenschap:
aria-controlskoppelt de knop aan de inhoud
Praktische tips voor het testen
Om je implementatie te controleren:
- Browser-inspectiehulpmiddelen: De toegankelijkheidsinspector in Firefox of de accessibility tree en Lighthouse in Chrome
- Test zelf met een schermlezer: NVDA (Windows) of VoiceOver (Mac) zijn goede opties
- Test met automatische tools: axe DevTools (browser-extensie)
Conclusie
Door standaard HTML-elementen te gebruiken waar mogelijk en ARIA waar nodig, maak je je website toegankelijker voor iedereen. En het mooie is: veel van deze technieken verbeteren ook de gebruikerservaring voor mensen zonder functiebeperking.
Heb je nog vragen over WCAG 4.1.2 of andere succescriteria? Laat het me weten!