Als ontwikkelaar leg je vast wat hulptechnologie te horen krijgt. Een knop die echt een <button> is, een label dat aan het juiste veld hangt, een component dat doorgeeft of hij open of dicht staat. Zonder die code weet een schermlezer niet wat er op het scherm gebeurt.
Deze checklist bevat de punten die jij in je code oplost.
Let op: deze checklist is een hulpmiddel, geen norm. WCAG bestaat uit succescriteria die je per situatie beoordeelt, en dat laat zich niet volledig vertalen in afvinkpunten. Wil je zeker weten of iets aan de eisen voldoet? Dan heb je een toegankelijkheidsonderzoek nodig.
Afbeeldingen
Van toepassing als je code afbeeldingen, iconen of SVG’s bevat.
- Elke informatieve afbeelding heeft een tekstalternatief dat vertelt wat je ziet en welke informatie de afbeelding overbrengt.
- Elke decoratieve afbeelding heeft een leeg tekstalternatief.
- Elke afbeelding die als link of knop werkt, heeft een tekstalternatief dat het doel of de actie beschrijft.
- Elk
<img>-element heeft eenalt-attribuut. - Elk informatief of functioneel
<svg>-element heeftrole="img"en een<title>-element als eerste content. - Elk decoratief
<svg>-element heeftaria-hidden="true"enfocusable="false". - Elk
<input type="image">-element heeft eenalt-attribuut. - Gebruik je een image map? Dan heeft het
<img>-element metusemapeenalt-attribuut dat de hele image map beschrijft, en elk<area>-element eenalt-attribuut dat het linkdoel beschrijft. - Elk
<canvas>-element met een informatieve afbeelding heeftrole="img"en een toegankelijke naam. - Elke CAPTCHA heeft een tekstalternatief dat de functie beschrijft en een toegankelijk alternatief in dezelfde context.
- Elke knop en link met een afbeelding heeft een toegankelijke naam die de zichtbare tekst bevat.
Bediening
Van toepassing zodra er iets interactief is op de pagina.
- Verschijnt er content bij hover of focus? Dan is die te sluiten, blijft die bereikbaar met de aanwijzer en blijft die zichtbaar tot de gebruiker hem sluit.
- Elk interactief element is bereikbaar en bedienbaar met alleen het toetsenbord.
- Er staat geen onderdeel op de pagina waarin de toetsenbordfocus vast komt te zitten.
- Zijn er sneltoetsen met één teken? Dan zijn die uit te schakelen of aan te passen.
- De pagina heeft een skiplink die alle herhalende blokken overslaat en de focus naar het doel verplaatst.
- De focusvolgorde is logisch en volgt de visuele volgorde. Wijkt de focusvolgorde daar toch van af? Dan blijven de betekenis en de bedienbaarheid van de pagina behouden.
- Er staat geen element in de focusvolgorde dat voor alle gebruikers verborgen is.
- Elke ankerlink verplaatst de focus naar het doel van de link.
- Elk modaal dialoogvenster krijgt de focus bij openen, houdt de focus vast zolang het open is en geeft de focus bij sluiten terug aan het element dat het opende.
- Elke dynamische content verschijnt op een logische plek in de lees- en focusvolgorde.
- Elk bedieningselement heeft een zichtbare focusindicator. Gebruik je
outline: none? Dan vervang je die door een eigen focusstijl, in plaats van de focusindicator te verwijderen. - Er staat geen element op de pagina, zoals een sticky header of een modal, dat de focusindicator volledig bedekt.
- Werkt iets met een complex aanwijzergebaar, zoals swipe of pinch? Dan is er ook een alternatief met een enkele klik of tik.
- Er staat geen bedieningselement op de pagina dat de actie al bij
mousedownuitvoert. De actie zit opmouseupofclick, zodat de gebruiker kan annuleren. - Werkt een functie via bewegen, schudden of kantelen van het apparaat? Dan is er ook een alternatief via een bedieningselement, en is de bewegingsactivering uit te schakelen.
- Werkt iets met een sleepbeweging, zoals drag-and-drop? Dan is er ook een alternatief met een enkele klik of tik.
- Elk bedieningselement heeft een klikbaar gebied van minimaal 24 bij 24 CSS-pixels. Is dat niet haalbaar? Dan zit er genoeg ruimte rondom het element, of staat er elders op de pagina een gelijkwaardig element dat wel groot genoeg is.
- Er is geen focuswijziging die een onverwachte contextwijziging veroorzaakt, zoals een nieuw venster of een verzonden formulier.
Beweging
Van toepassing als de pagina bewegende of automatisch verschijnende content bevat.
- Elke automatisch bewegende, knipperende of scrollende content is te pauzeren, te stoppen of te verbergen.
- Er staat geen content op de pagina die meer dan 3 keer per seconde flitst.
Formulieren
Van toepassing als je een formulier bouwt, van een zoekveld tot een aanvraagproces.
- Elk invoerveld is aan een label gekoppeld met het
for-attribuut, of het veld staat in het<label>-element. - Elk label is zichtbaar en beschrijvend.
- Elke groep velden die bij elkaar hoort, zoals een set keuzerondjes, heeft een gezamenlijke naam via
<fieldset>met een<legend>. - Elk verplicht veld is programmatisch aangeduid, bijvoorbeeld met
requiredofaria-required. - Elk veld dat persoonlijke gegevens verzamelt, zoals naam, e-mailadres en adres, heeft het juiste
autocomplete-attribuut. - Heeft een formulier een tijdslimiet? Dan is die uit te schakelen, aan te passen of te verlengen.
- Elk label beschrijft duidelijk wat er in het invoerveld moet.
- Er is geen invoerveld waarvan een wijziging een onverwachte contextwijziging veroorzaakt, zoals een formulier dat automatisch verzendt.
- Stelt een veld eisen aan het formaat? Dan staan die eisen in het label of in een instructie bij het veld, bijvoorbeeld “dd-mm-jjjj”.
- Zijn er aanvullende instructies bij een veld? Dan zijn die zichtbaar.
- Elke foutmelding is met
aria-describedbyaan het juiste veld gekoppeld en benoemt welk veld fout is ingevuld en wat de fout is. - Er is geen foutmelding die alleen uit de standaardvalidatie van de browser komt.
- Elke foutmelding bevat een suggestie voor de juiste invoer, tenzij dat een beveiligingsrisico oplevert.
- Elke foutmelding benoemt het verwachte invoerformaat, het verwachte bereik of de toegestane waarden, en bij een automatisch aangepaste waarde naar welke waarde is omgezet.
- Heeft een formulier wettelijke of financiële gevolgen? Dan kan de gebruiker de invoer controleren, corrigeren of bevestigen.
- Er is geen veld dat informatie opnieuw vraagt die de gebruiker eerder in hetzelfde proces invulde. Die informatie is vooringevuld of te selecteren.
- Er is geen inlogstap die een cognitieve test vraagt, zoals een puzzel of het onthouden van een wachtwoord. Een wachtwoordmanager, passkey of verificatiecode werkt.
- Elk statusbericht, zoals “opgeslagen” of “3 resultaten gevonden”, gaat naar hulptechnologie via een live region, zonder dat de focus verplaatst.
Media
Van toepassing als de pagina een mediaspeler, filmpje of geluidsfragment bevat.
- Start er automatisch geluid dat langer dan 3 seconden duurt? Dan is het te pauzeren, te stoppen of te dempen met een bedieningselement.
- Elke mediaspeler heeft bedieningselementen met een toegankelijke naam en is bedienbaar met alleen het toetsenbord.
Navigeren
Van toepassing op elke pagina die je bouwt.
- Elke pagina heeft in de
<head>een<title>-element met een unieke, beschrijvende paginatitel. - Laadt er een nieuwe pagina of verandert de weergave, ook in een single page application? Dan verandert de paginatitel mee.
- Elke link in een template of component heeft een toegankelijke naam die het doel beschrijft. Wat visueel één link is, is in de code ook één link-element.
- Er zijn minstens twee manieren om een pagina te vinden, bijvoorbeeld een zoekfunctie en een sitemap.
- Elke navigatie staat op elke pagina op dezelfde positie.
- Elk hulpmechanisme, zoals een contactpagina, chatfunctie of veelgestelde vragen, staat op elke pagina op dezelfde positie.
Techniek
Van toepassing op alle code die je oplevert.
- Er staat geen
role="presentation"ofrole="none"op content met semantische betekenis. - Er is geen witruimte of los teken gebruikt om letterafstand, kolommen of tabellen na te bootsen.
- De volgorde in de code komt overeen met de visuele volgorde.
- Er staat geen automatische paginavernieuwing binnen 20 uur op de pagina, tenzij de gebruiker die kan uitzetten, aanpassen of verlengen.
- Er staat geen content op de pagina die vanzelf verschijnt en weer verdwijnt zonder dat de gebruiker daar controle over heeft, bijvoorbeeld een banner die na een paar seconden automatisch sluit.
- Elke toegankelijke naam van een bedieningselement bevat de zichtbare tekst.
- Dezelfde functie heeft in de code steeds dezelfde benaming.
- Elk bedieningselement heeft een toegankelijke naam, een passende rol en, waar van toepassing, een waarde.
- Elk bedieningselement geeft zijn toestand en eigenschappen door in de code, bijvoorbeeld ingedrukt, uitgeklapt, geselecteerd of uitgeschakeld.
- Elk maatwerkcomponent heeft een passende rol en geeft zijn toestand, eigenschappen en waarde door in de code.
- Er staat geen bedieningselement in een ander bedieningselement.
- Elk
<iframe>-element heeft een toegankelijke naam. - Elk ARIA-attribuut is correct gebruikt en verwijst naar een bestaand element.
Tekst
Van toepassing op elke template en elk component met tekstuele content.
- Elke kop is opgemaakt met
<h1>tot<h6>. - De volgorde van de koppen staat de betekenis van de pagina niet in de weg.
- Elke lijst is opgemaakt met
<ol>of<ul>:<ol>voor een geordende lijst,<ul>voor een ongeordende lijst. - Elke datatabel gebruikt
<table>,<th>en<td>op de juiste manier. Een complexere tabel heeftscope="col"ofscope="row". - Elke gegevenscel is programmatisch gekoppeld aan de bijbehorende tabelkoppen.
- Er staat geen
<th>,<caption>,summary,headersofscopein een presentatietabel. - Elke pagina heeft een
lang-attribuut op het<html>-element met de hoofdtaal van de pagina. - Elke tekst in een andere taal dan de hoofdtaal heeft een eigen
lang-attribuut.
Weergave
Van toepassing op alle CSS die je oplevert.
- De pagina werkt in staande én liggende weergave. Er is geen vaste weergavestand geforceerd.
- Er is geen melding die de content blokkeert met het verzoek om het apparaat te draaien.
- Tekst is tot 200% te vergroten zonder dat content of functionaliteit verdwijnt.
- Er staat niks in de code dat pinch-to-zoom uitschakelt, zoals
user-scalable=noofmaximum-scale=1in de viewport meta tag. - Alle content blijft leesbaar en bedienbaar bij een breedte van 320 CSS-pixels (oftewel 400% zoom op een scherm van 1280 pixels). Bij content die bedoeld is om horizontaal te scrollen, geldt een minimale hoogte van 256 CSS-pixels.
- Na inzoomen hoeft niemand zowel horizontaal als verticaal te scrollen.
- Content blijft werken als de gebruiker de tekstafstand aanpast, zoals de regelhoogte, de letterafstand en de woordafstand.
- De pagina verhindert niet dat de gebruiker zelf stijleigenschappen aanpast, bijvoorbeeld met
!importantop regelhoogte, letterafstand of woordafstand, of met JavaScript dat aangepaste waarden terugzet.