WCAG-succescriterium 3.3.2 Labels of instructies
Niveau A
Een bezoeker die een formulier invult, ziet bij elk veld een label dat beschrijft wat er wordt verwacht.
Bijvoorbeeld: “Voornaam”, “E-mailadres”, “Postcode”. Waar het invoerformaat niet vanzelfsprekend is, staat er een instructie bij: “Gebruik het formaat dd-mm-jjjj” of “Minimaal 8 tekens”. Zo weet de bezoeker wat er moet worden ingevuld voordat er een fout ontstaat.
Labels zijn zichtbaar en blijven zichtbaar terwijl de bezoeker het veld invult. Zodra het veld focus krijgt, leest een screenreader het label voor — of de alt-tekst als een icoon als label dient. Instructies staan bij het veld en niet alleen in een inleidende tekst bovenaan het formulier.
Veelgemaakte fouten
Invoerveld heeft geen zichtbaar label
Elk invoerveld heeft een zichtbaar label dat beschrijft wat er moet worden ingevuld. Een veld zonder label dwingt de bezoeker om uit de context te raden wat er wordt verwacht. Een screenreader leest alleen “invoerveld” voor zonder verdere informatie.
Hoe te testen: bekijk het formulier. Heeft elk veld een zichtbaar label? Klik op het label.
Niet doen
Invoerveld zonder zichtbaar label
<astro-static-slot>() => createVNode("p", {
children: createVNode("input", {
type: "email",
placeholder: "E-mailadres"
})
})</astro-static-slot>
Doen
Invoerveld met een zichtbaar en gekoppeld label
<astro-static-slot>() => createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-email",
children: "E-mailadres"
}), createVNode("br", {}), createVNode("input", {
type: "email",
id: "vis-email"
})]
})</astro-static-slot>
Wie kan dit oplossen: een developer voegt een <label>-element toe dat via for en id is gekoppeld aan het invoerveld.
Label is alleen beschikbaar voor screenreaders
Een invoerveld heeft een aria-label of een visueel verborgen <label>, maar geen zichtbaar label op het scherm. Een screenreader leest het label voor, maar een ziende bezoeker ziet niet wat er in het veld moet worden ingevuld. Dit succescriterium vereist dat labels zichtbaar zijn voor iedereen, niet alleen voor hulpsoftware.
Hoe te testen: bekijk het formulier zonder hulpsoftware. Heeft elk veld een zichtbaar label? Inspecteer de velden in de DevTools: staat het label in een aria-label of in een element met class="sr-only" of vergelijkbare visueel verborgen CSS?
Niet doen
Label alleen beschikbaar via aria-label
<input type="text" aria-label="Zoekterm" placeholder="Zoeken..." />
Niet doen
Label visueel verborgen met sr-only
<label for="zoek" class="sr-only">
Zoekterm
</label>
<input type="text" id="zoek" placeholder="Zoeken..." />
Doen
Zichtbaar label voor iedereen
<label for="zoek">Zoekterm</label>
<input type="text" id="zoek" />
Wie kan dit oplossen: een developer vervangt het verborgen label door een zichtbaar label. Een designer maakt ruimte in het ontwerp voor zichtbare labels.
Zoekveld heeft alleen een icoon als label en dat icoon is niet zichtbaar voor iedereen
Een zoekveld zonder tekstlabel kan acceptabel zijn als de context ondubbelzinnig is — bijvoorbeeld een vergrootglasicoon naast een zoekknop. Maar als dat icoon een contrast van minder dan 3:1 heeft met de achtergrond, is het niet zichtbaar voor bezoekers met verminderd gezichtsvermogen. Het veld heeft dan voor die bezoekers geen herkenbare aanduiding.
Hoe te testen: meet het contrast van het vergrootglasicoon ten opzichte van de achtergrond. Is het lager dan 3:1? Dan is het icoon niet voor iedereen zichtbaar en functioneert het niet als label.
Niet doen
Zoekveld met een vergrootglasicoon dat onvoldoende contrast heeft
<astro-static-slot>() => createVNode("p", {
style: {
display: "flex",
alignItems: "center",
gap: "4px"
},
children: [createVNode("input", {
type: "search",
"aria-label": "Zoeken",
placeholder: "Zoeken..."
}), createVNode("button", {
style: {
background: "none",
border: "none",
padding: "4px"
},
children: createVNode("svg", {
width: "20",
height: "20",
viewBox: "0 0 24 24",
fill: "#CCCCCC",
"aria-hidden": "true",
children: createVNode("path", {
d: "M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
})
})
})]
})</astro-static-slot>
Doen
Zoekveld met een zichtbaar tekstlabel
<astro-static-slot>() => createVNode("p", {
style: {
display: "flex",
alignItems: "center",
gap: "4px"
},
children: [createVNode("label", {
htmlFor: "vis-zoek",
children: "Zoeken"
}), createVNode("input", {
type: "search",
id: "vis-zoek"
}), createVNode("button", {
style: {
background: "none",
border: "none",
padding: "4px"
},
children: createVNode("svg", {
width: "20",
height: "20",
viewBox: "0 0 24 24",
fill: "#333333",
"aria-hidden": "true",
children: createVNode("path", {
d: "M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
})
})
})]
})</astro-static-slot>
Doen
Zoekveld met een zoekknop die tekst bevat
<astro-static-slot>() => createVNode("p", {
style: {
display: "flex",
alignItems: "center",
gap: "4px"
},
children: [createVNode("input", {
type: "search",
"aria-label": "Zoeken"
}), createVNode("button", {
children: "Zoeken"
})]
})</astro-static-slot>
Wie kan dit oplossen: een developer voegt een zichtbaar tekstlabel toe, of een zoekknop met zichtbare tekst. Een designer zorgt dat het icoon voldoende contrast heeft als het de enige visuele aanduiding is.
Placeholder wordt gebruikt als label
Een placeholder verdwijnt zodra de bezoeker begint te typen. De bezoeker weet dan niet meer wat er in het veld moet worden ingevuld. Placeholders zijn niet bedoeld als vervanging van een label.
Hoe te testen: begin met typen in een veld dat alleen een placeholder heeft. Verdwijnt de placeholder zodra je begint te typen?
Niet doen
Placeholder als enige aanduiding van het veld
<astro-static-slot>() => createVNode(Fragment, {
children: [createVNode("p", {
children: createVNode("input", {
type: "text",
placeholder: "Voornaam"
})
}), createVNode("p", {
children: createVNode("input", {
type: "text",
placeholder: "Achternaam"
})
}), createVNode("p", {
children: createVNode("input", {
type: "email",
placeholder: "E-mailadres"
})
})]
})</astro-static-slot>
Doen
Zichtbaar label met een placeholder als aanvullend voorbeeld
<astro-static-slot>() => createVNode(Fragment, {
children: [createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-voornaam",
children: "Voornaam"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-voornaam",
placeholder: "bijv. Fatima"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-achternaam",
children: "Achternaam"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-achternaam",
placeholder: "bijv. De Vries"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-email2",
children: "E-mailadres"
}), createVNode("br", {}), createVNode("input", {
type: "email",
id: "vis-email2",
placeholder: "bijv. naam@voorbeeld.nl"
})]
})]
})</astro-static-slot>
Wie kan dit oplossen: een developer voegt permanent zichtbare labels toe aan de velden. De placeholders kunnen blijven staan als aanvullend voorbeeld.
Verwacht invoerformaat ontbreekt
Een veld verwacht een specifiek formaat, maar dat staat nergens vermeld. Een datumveld zonder instructie laat de bezoeker raden: is het dd-mm-jjjj of mm/dd/yyyy? Een telefoonnummerveld zonder instructie: met of zonder landcode? Met spaties of aaneengesloten?
Hoe te testen: bekijk elk veld dat een specifiek formaat verwacht. Staat het verwachte formaat bij het veld, als placeholder of als instructietekst?
Niet doen
Datumveld zonder formaatinstructie
<label for="geboortedatum">Geboortedatum</label>
<input type="text" id="geboortedatum" />
Doen
Datumveld met formaatinstructie
<label for="geboortedatum">Geboortedatum</label>
<input type="text" id="geboortedatum" aria-describedby="datum-hint" placeholder="dd-mm-jjjj" />
<p id="datum-hint" class="hint">
Gebruik het formaat dd-mm-jjjj, bijvoorbeeld 15-03-1990
</p>
De instructie is via aria-describedby gekoppeld aan het veld, zodat een screenreader het voorleest wanneer het veld focus krijgt.
Wie kan dit oplossen: een developer voegt een instructie toe bij het veld en koppelt die via aria-describedby.
Verplichte velden zijn niet gemarkeerd
Een formulier heeft verplichte en optionele velden, maar er is geen aanduiding welke verplicht zijn. De bezoeker ontdekt het pas na het verzenden. Markeer verplichte velden met een zichtbare aanduiding en het required-attribuut of aria-required="true".
Hoe te testen: bekijk het formulier. Kun je voor het invullen zien welke velden verplicht zijn? Is er een legenda die het sterretje of de aanduiding “(verplicht)” verklaart?
Niet doen
Verplicht veld zonder aanduiding
<astro-static-slot>() => createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-naam1",
children: "Naam"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-naam1"
})]
})</astro-static-slot>
Doen
Verplicht veld met zichtbare aanduiding en required-attribuut
<astro-static-slot>() => createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-naam2",
children: ["Naam ", createVNode("span", {
"aria-hidden": "true",
children: "*"
})]
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-naam2",
required: true
})]
})</astro-static-slot>
Doen
Verplicht veld met het woord 'verplicht' in het label
<astro-static-slot>() => createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-naam3",
children: "Naam (verplicht)"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-naam3",
required: true
})]
})</astro-static-slot>
Als de meeste velden verplicht zijn, markeer dan de optionele velden met “(optioneel)” in plaats van alle verplichte velden met een sterretje.
Wie kan dit oplossen: een developer voegt de markering toe aan de labels en het required-attribuut aan de velden.
Instructie staat alleen bovenaan het formulier
Een formulier begint met een blok instructietekst dat uitlegt hoe het formulier moet worden ingevuld. Verder in het formulier ontbreken instructies bij de individuele velden. Bij een lang formulier is de bezoeker de instructie vergeten tegen de tijd dat het betreffende veld in beeld komt.
Hoe te testen: scroll door een lang formulier. Zijn de instructies beschikbaar bij de velden waar ze relevant zijn, of moet je terugscrollen naar het begin?
Niet doen
Instructies alleen bovenaan het formulier
<p>Vul alle datums in als dd-mm-jjjj. Telefoonnummers zonder spaties.</p>
<!-- Verderop in het formulier, zonder herhaling: -->
<label for="startdatum">Startdatum</label>
<input type="text" id="startdatum" />
<label for="telefoon">Telefoonnummer</label>
<input type="tel" id="telefoon" />
Doen
Instructies bij de individuele velden
<label for="startdatum">Startdatum</label>
<input type="text" id="startdatum" aria-describedby="startdatum-hint" />
<p id="startdatum-hint" class="hint">Gebruik het formaat dd-mm-jjjj</p>
<label for="telefoon">Telefoonnummer</label>
<input type="tel" id="telefoon" aria-describedby="telefoon-hint" />
<p id="telefoon-hint" class="hint">10 cijfers, zonder spaties</p>
Wie kan dit oplossen: een developer plaatst de instructies bij de velden en koppelt ze via aria-describedby.
Groep invoervelden zonder groepslabel
Een formulier heeft een sectie met adresvelden: straat, huisnummer, postcode, plaats. Visueel is duidelijk dat ze bij elkaar horen, maar in de code ontbreekt een groepslabel. Een screenreader leest de velden los van elkaar voor zonder de context van de groep.
Dit komt ook voor bij betalingsformulieren (kaartnummer, vervaldatum, CVC) en bij persoonlijke gegevens (voornaam, achternaam, geboortedatum).
Hoe te testen: ga met een screenreader door een groep gerelateerde velden. Wordt de groepsnaam voorgelezen bij elk veld?
Niet doen
Adresvelden zonder groepering
<astro-static-slot>() => createVNode(Fragment, {
children: [createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-straat1",
children: "Straat"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-straat1"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-huisnummer1",
children: "Huisnummer"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-huisnummer1"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-postcode1",
children: "Postcode"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-postcode1"
})]
})]
})</astro-static-slot>
Doen
Adresvelden gegroepeerd met fieldset en legend
<astro-static-slot>() => createVNode("fieldset", {
children: [createVNode("legend", {
children: "Factuuradres"
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-straat2",
children: "Straat"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-straat2"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-huisnummer2",
children: "Huisnummer"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-huisnummer2"
})]
}), createVNode("p", {
children: [createVNode("label", {
htmlFor: "vis-postcode2",
children: "Postcode"
}), createVNode("br", {}), createVNode("input", {
type: "text",
id: "vis-postcode2"
})]
})]
})</astro-static-slot>
Wie kan dit oplossen: een developer groepeert de velden met <fieldset> en <legend>.
Hoe te testen
Voor iedereen
- Bekijk elk invoerveld in het formulier. Heeft elk veld een zichtbaar label dat beschrijft wat er wordt verwacht?
- Controleer of verplichte velden zijn gemarkeerd. Weet je voor het invullen welke velden verplicht zijn?
- Controleer velden die een specifiek formaat verwachten. Staat het verwachte formaat bij het veld?
- Begin te typen in een veld. Verdwijnt het label? Zo ja, weet je nog wat er wordt verwacht?
Voor developers
- Inspecteer elk invoerveld in de DevTools. Heeft elk veld een gekoppeld
<label>viaforenid? Zoek naar<input>,<select>en<textarea>zonder gekoppeld label. - Controleer of instructies via
aria-describedbyzijn gekoppeld aan het bijbehorende veld. Navigeer met een screenreader naar het veld en controleer of de instructie wordt voorgelezen. - Controleer of verplichte velden het
required-attribuut ofaria-required="true"hebben. - Controleer groepen gerelateerde velden. Zijn ze gegroepeerd met
<fieldset>en<legend>? - Test labels die in het veld zweven (floating labels) bij 200% en 400% zoom. Blijven ze zichtbaar en leesbaar?
- Gebruik axe DevTools of WAVE voor een eerste scan. Ontbrekende labels worden automatisch herkend. Of de labels inhoudelijk duidelijk zijn, controleer je handmatig.
Gerelateerde succescriteria
- 1.3.1 Info en relaties: labels zijn programmatisch gekoppeld aan hun invoerveld. Lees hier meer als de koppeling tussen label en veld ontbreekt in de code.
- 3.3.1 Foutidentificatie: duidelijke labels en instructies voorkomen fouten. Als er toch fouten ontstaan, beschrijft de foutmelding wat er mis is.
- 3.3.3 Foutsuggestie: als het systeem weet hoe een fout gecorrigeerd kan worden, biedt het een suggestie aan. Instructies bij het veld verminderen de noodzaak voor suggesties achteraf.
- 2.5.3 Label in naam: de zichtbare tekst van een label komt overeen met de toegankelijke naam. Relevant als je
aria-labelgebruikt dat afwijkt van het zichtbare label. - 1.4.3 Contrast (minimum): labels en instructietekst voldoen aan de minimale contrasteis van 4,5:1. Lichtgrijze placeholders halen die eis niet.
Gerelateerde NL Design System-richtlijnen
- Formulieren: Labels.
- Formulieren: Descriptions.
- Formulieren: Veldtypen.
Relevante bronnen
- WCAG 2.2: Succescriterium 3.3.2 Labels of instructies — de officiële Nederlandstalige vertaling van het succescriterium. Gebruik dit als referentie voor de exacte eisen.
- Understanding SC 3.3.2: Labels or Instructions — de W3C-uitleg bij het succescriterium, met technieken en voorbeelden (Engels).
- Accessible form validation — uitgebreide gids van Smashing Magazine over toegankelijke formulieren, inclusief labels en instructies (Engels).
Gebruikersonderzoek
Heb je gebruikersonderzoek gedaan dat betrekking heeft op dit succescriterium en wil je dit delen? Kijk eens bij Gebruikersonderzoeken delen op gebruikersonderzoeken.nl.
W3C referenties
- Engelse tekst van het WCAG-succescriterium: 3.3.2 Labels or Instructions.
- Nederlandse vertaling van het WCAG-succescriterium: 3.3.2 Labels of instructies.
- Engelstalige informatie op How to Meet WCAG: Quick Reference 3.3.2 Labels or Instructions.
- Engelstalige toelichting: Understanding SC 3.3.2 Labels or Instructions.
Belangrijk: De richtlijnen van NL Design System zijn geen wettelijke verplichting
De richtlijnen van NL Design System zijn niet wettelijk verplicht en zijn geen vervanging voor de wettelijk geldende WCAG 2.1 specificatie.
Ons doel is om praktische uitleg en voorbeelden te geven die helpen bij het toegankelijk inzetten van de NL Design System componenten, patronen en richtlijnen. We doen dat op basis van een interpretatie van de nieuwe WCAG 2.2 specificatie.
Weten waar je volgens de wet aan moet voldoen? Ga dan naar wat is verplicht van DigiToegankelijk.
Help richtlijn verbeteren
Aanvullingen of opmerkingen?
Deze pagina’s over WCAG worden onderhouden door NL Design System. Heb je aanvullingen of opmerkingen? Deel je mening op GitHub.