WCAG 2.2 checklist voor ontwikkelaars

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 een checklist. Wil je zeker weten of iets aan de eisen voldoet? Dan heb je een toegankelijkheidsonderzoek nodig.

Altijd controleren

Deze punten gelden voor alle code die je oplevert.

Koppen en taal

  • Elke kop is opgemaakt met <h1> tot <h6>.
  • Elke kop beschrijft de tekst die eronder staat.
  • De volgorde van de koppen staat de betekenis van de pagina niet in de weg.
  • Er staat geen tekst op de pagina die als kop is opgemaakt om hem groter of dikker te maken.
  • 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.

Semantiek, rollen en namen

  • 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).
  • Elke toegankelijke naam van een bedieningselement bevat de zichtbare tekst.
  • Dezelfde functie heeft in de code steeds dezelfde benaming.
  • Er staat geen bedieningselement in een ander bedieningselement.
  • Er staat geen role="presentation" of role="none" op content met semantische betekenis.
  • Elk ARIA-attribuut is correct gebruikt en verwijst naar een bestaand element.
  • 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.
  • Elk <iframe>-element heeft een toegankelijke naam.

Toetsenbord en focus

  • 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.
  • De pagina heeft een skiplink die alle herhalende blokken overslaat.
  • De skiplink is altijd zichtbaar of wordt zichtbaar bij toetsenbordfocus.
  • 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 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.
  • Er is geen focuswijziging die een onverwachte contextwijziging veroorzaakt, zoals een nieuw venster of een verzonden formulier.
  • Zijn er sneltoetsen met één teken? Dan zijn die uit te schakelen of aan te passen.

Aanwijzer en klikgebied

  • 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 staat geen bedieningselement op de pagina dat de actie al bij mousedown uitvoert. De actie zit op mouseup of click, zodat de gebruiker kan annuleren.

Paginatitel en navigatie

  • Elke pagina heeft in de <head> een <title>-element met een beschrijvende paginatitel.
  • Laadt er een nieuwe pagina of verandert de weergave, ook in een single page application? Dan verandert de paginatitel mee.
  • Elke link heeft een toegankelijke naam die het doel beschrijft uit de linktekst of de omringende context. 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.

Weergave en zoomen

  • 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=no of maximum-scale=1 in 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 !important op regelhoogte, letterafstand of woordafstand, of met JavaScript dat aangepaste waarden terugzet.

Automatisch gedrag

  • 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.

Alleen als dit van toepassing is

Ga per onderwerp na of het op jouw code van toepassing is. Zo niet? Sla het dan over.

Afbeeldingen, SVG’s en iconen

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.
  • Elke knop en link met een afbeelding heeft een toegankelijke naam die de zichtbare tekst bevat.
  • Elk <img>-element heeft een alt-attribuut.
  • Elk <input type="image">-element heeft een alt-attribuut.
  • Elk informatief of functioneel <svg>-element heeft role="img" en een <title>-element als eerste content.
  • Elk decoratief <svg>-element heeft aria-hidden="true" en focusable="false".
  • Elk <canvas>-element met een informatieve afbeelding heeft role="img" en een toegankelijke naam.
  • Gebruik je een image map? Dan heeft het <img>-element met usemap een alt-attribuut dat de hele image map beschrijft, en elk <area>-element een alt-attribuut dat het linkdoel beschrijft.
  • Elk symbool, unicode-teken en stukje ASCII-art in de tekst heeft een tekstalternatief.
  • Elke CAPTCHA heeft een tekstalternatief dat de functie beschrijft en heeft een toegankelijk alternatief in dezelfde context.

Lijsten en tabellen

Van toepassing als je code een lijst of een tabel bevat.

  • 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 heeft scope="col" of scope="row".
  • Elke gegevenscel is programmatisch gekoppeld aan de bijbehorende tabelkoppen.
  • Presentatietabellen worden verborgen met role="presentation".
  • Er staat geen <th>, <caption>, summary, headers of scope in een presentatietabel.

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 beschrijft duidelijk wat er in het invoerveld moet worden ingevuld.
  • Elk label is zichtbaar.
  • 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 required of aria-required.
  • Elk verplicht veld is duidelijk gemarkeerd, in het label of op formulierniveau, bijvoorbeeld met “(verplicht)” of een sterretje met uitleg.
  • 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.
  • Elk veld dat persoonlijke gegevens verzamelt, zoals naam, e-mailadres en adres, heeft het juiste autocomplete-attribuut.
  • 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 invoerveld waarvan een wijziging een onverwachte contextwijziging veroorzaakt, zoals een formulier dat automatisch verzendt.
  • Elke foutmelding benoemt welk veld fout is ingevuld en wat de fout is.
  • Elke foutmelding is met aria-describedby aan het juiste veld gekoppeld.
  • 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.
  • Er is geen foutmelding die alleen uit de standaardvalidatie van de browser komt.
  • Elk statusbericht, zoals “opgeslagen” of “3 resultaten gevonden”, gaat naar hulptechnologie via een live region, zonder dat de focus verplaatst.
  • Elke verzendknop heeft een tekst die beschrijft wat er gebeurt als je erop klikt.
  • Heeft een formulier een tijdslimiet? Dan is die uit te schakelen, aan te passen of te verlengen.
  • Heeft een formulier wettelijke of financiële gevolgen? Dan kan de gebruiker de invoer controleren, corrigeren of bevestigen.
  • Er is geen inlogstap die een cognitieve test vraagt, zoals een puzzel of het onthouden van een wachtwoord. Een wachtwoordmanager, passkey of verificatiecode werkt.

Componenten en dynamische content

Van toepassing als je een maatwerkcomponent bouwt, of content die verschijnt en verdwijnt.

  • Elk maatwerkcomponent heeft een passende rol en geeft zijn toestand, eigenschappen en waarde door in de code.
  • 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 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.

Media

Van toepassing als de pagina een mediaspeler, filmpje of geluidsfragment bevat.

  • Elke mediaspeler heeft bedieningselementen met een toegankelijke naam en is bedienbaar met alleen het toetsenbord.
  • Start er automatisch geluid dat langer dan 3 seconden duurt? Dan is het te pauzeren, te stoppen of te dempen met een bedieningselement.

Beweging

Van toepassing als de pagina bewegende 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.

Gebaren en slepen

Van toepassing als je bediening bouwt via een aanwijzergebaar, een sleepbeweging of het bewegen van het apparaat.

  • Werkt iets met een complex aanwijzergebaar, zoals swipe of pinch? Dan is er ook een alternatief met een enkele klik of tik.
  • Werkt iets met een sleepbeweging, zoals drag-and-drop? Dan is er ook een alternatief met een enkele klik of tik.
  • 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.

Werk je samen aan een project?

Toegankelijkheid is teamwerk. Deel deze checklists met je collega’s:

Wil je een breder overzicht van wat toegankelijkheid voor ontwikkelaars betekent? Ga naar WCAG voor ontwikkelaars. Of lees het complete overzicht van alle succescriteria uit WCAG 2.2, beknopt en helder uitgelegd.