Toegankelijke formulieren volgens WCAG (praktische implementatie)
Formulieren zijn technisch simpel maar gaan in de praktijk vaak fout: ontbrekende of onduidelijke labels, onsamenhangende foutmeldingen, slechte keyboard- en screenreader-ervaringen en verkeerd gebruik van ARIA. Dat kost gebruikers tijd, levert conversieverlies op en zorgt voor juridische risico’s. Wij richten ons op directe oplossingen die je vandaag kunt implementeren: heldere markup, voorspelbare foutafhandeling en testbare scripts.
Dit artikel biedt stap-voor-stap codevoorbeelden (HTML/CSS/JS), concrete regels voor ontwerp en content, en een testplan dat je direct kunt uitvoeren. Gebruik onze WCAG checker/validator om je pagina live te testen, download onze plugin voor geautomatiseerde scans en neem contact op via het formulier — vragen beantwoordt ons team binnen 24 uur.
Het probleem in de praktijk
Veelvoorkomende fouten
- Labels ontbreken of zijn visueel niet gekoppeld aan inputs (geen for/id relatie).
- Foutmeldingen zijn visueel maar niet in de toegankelijkheidsboom (screenreaders missen ze).
- Focus-management ontbreekt: na submit blijft de gebruiker in het ongewisse en assistive technology verliest context.
- ARIA wordt onjuist toegepast (role misuse, aria-hidden op essentiële elementen).
- Complexe widgets zoals datumpickers missen keyboardbediening en labels.
Waarom dit vaak gebeurt
- Designers en developers denken vanuit visueel ontwerp, niet vanuit assistive workflows.
- Ontbreken van een standaard component-library met correcte ARIA en tests.
- Content-editors plakken foutmeldingen als afbeelding of gewone tekst zonder semantiek.
Zo los je dit op in code
Fundamentele HTML-structuur: semantic labels en hintteksten
Gebruik altijd een label gekoppeld via for/id en voeg een helptekst toe met aria-describedby. Voor required velden gebruik required attribuut en een visuele indicator die ook voor screenreaders zichtbaar is.
<form id="signupForm" novalidate>
<div class="form-row">
<label for="email">E-mailadres <span aria-hidden="true">*</span><span class="sr-only">verplicht</span></label>
<input id="email" name="email" type="email" required aria-describedby="emailHelp emailError" />
<div id="emailHelp" class="hint">We sturen alleen belangrijke berichten</div>
<div id="emailError" class="error" aria-live="polite"></div>
</div>
<button type="submit">Aanmelden</button>
</form>
CSS: visually-hidden class en duidelijke foutstijl
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.error{color:#b00020;margin-top:0.25rem;}
input[aria-invalid="true"]{outline:2px solid #b00020;}
JavaScript: client-side validatie, focus management en ARIA updates
Gebruik progressive enhancement: laat browser-validatie werken, maar voeg JS toe voor consistente foutmeldingen, aria-invalid en focus naar de eerste fout.
document.getElementById('signupForm').addEventListener('submit', function(e){
e.preventDefault();
var form = e.target;
var firstInvalid = null;
var fields = form.querySelectorAll('input,select,textarea');
fields.forEach(function(f){
var errorEl = document.getElementById(f.id + 'Error');
if(!f.checkValidity()){
f.setAttribute('aria-invalid','true');
if(errorEl) errorEl.textContent = f.validationMessage || 'Controleer dit veld';
if(!firstInvalid) firstInvalid = f;
} else {
f.removeAttribute('aria-invalid');
if(errorEl) errorEl.textContent = '';
}
});
if(firstInvalid){
firstInvalid.focus();
// extra: verplaats focus naar een algemene foutcontainer indien gewenst
return false;
}
// submit via fetch of gewoon form.submit()
form.submit();
}, false);
Server-side foutmeldingen: maak ze semantisch
Wanneer server-side validatie faalt, geef een status 400 en stuur een JSON met velderrors. Render deze terug met dezelfde id’s en update aria-live gebieden.
{ "errors": { "email": "Dit e-mailadres is al in gebruik", "password": "Wachtwoord te kort" } }
Front-end voorbeeld om serverfouten te tonen:
function renderServerErrors(errors){
Object.keys(errors).forEach(function(name){
var el = document.getElementById(name);
var err = document.getElementById(name + 'Error');
if(el){ el.setAttribute('aria-invalid','true'); }
if(err){ err.textContent = errors[name]; }
});
var first = document.querySelector('[aria-invalid="true"]');
if(first) first.focus();
}
Checklist voor developers
- Labels: elk input heeft een <label for=”id”> of aria-label/aria-labelledby als visueel verborgen.
- Hints: gebruik aria-describedby voor helpteksten en foutmeldingen.
- Fouten: foutberichten in element met role=”alert” of aria-live=”polite” en gekoppeld via id.
- Keyboard: alle controls zijn focusbaar en werken zonder muis.
- Focus: bij fout na submit focus naar eerste foutveld.
- ARIA: gebruik ARIA alleen als native HTML niet volstaat; avoid role misuse.
- Server: return JSON met veld-fouten en koppel deze aan inputs.
- Automatiseer: draai onze WCAG checker/validator en installeer de WCAGtool plugin voor CI.
Tips voor designers en redacties
Ontwerpregels
- Zorg voor voldoende kleurcontrast van labels en fouten (controleer met onze checker).
- Gebruik consistente plaatsing: fout onder het veld of boven het formulier, niet beiden.
- Maak required-indicator ook tekstueel beschikbaar (bijv. “verplicht”).
Contentrichtlijnen
- Houd foutmeldingen kort, concreet en actiegericht: “Vul een geldig e-mailadres in” vs “Ongeldige invoer”.
- Vermijd vage meldingen: geef aan wat fout is en hoe te herstellen.
- Schrijf helpteksten met concrete voorbeelden en formaat (bijv. “DD-MM-JJJJ”).
Component libraries
Lever componenten met ingebouwde ARIA, tests en documentatie. Wij leveren een voorbeeldcomponent die je kunt plakken:
<template id="wcag-input">
<label part="label"><slot name="label"></slot></label>
<input part="input"></input>
<div part="error" aria-live="polite"></div>
</template>
Hoe test je dit?
Automatische tests
- Draai onze WCAG checker/validator op de live pagina voor snelle feedback.
- Integreer de WCAGtool plugin in je CI-pipeline voor regressiecontrole.
- Gebruik axe-core of Lighthouse in je test-suite en verifieer aria-live en focus-management via end-to-end tests.
Handmatige tests (quicklist)
- Toetsenbordvolgorde: navigeer met Tab en Shift+Tab, alle controls moeten bereikbaar zijn in logische volgorde.
- Screenreader-run: met NVDA/VoiceOver lees je labels en foutmeldingen voor en zorg dat aria-describedby en aria-live werken.
- Verwijder CSS: check of formulier nog steeds bruikbaar is zonder styling (structurele toegankelijkheid).
- Simuleer langzame netwerken: zorg dat serverfouten correct terugkomen en de gebruiker feedback krijgt.
E2E voorbeeld (Puppeteer/Playwright) voor focus na submit
// Playwright voorbeeld
const { test, expect } = require('@playwright/test');
test('focus moves to first invalid field', async ({ page }) => {
await page.goto('https://jouwsite.example/form');
await page.click('button[type="submit"]');
const focused = await page.evaluate(() => document.activeElement.id);
expect(focused).toBe('email'); // pas aan naar jouw eerste required veld id
});
Checklist testresultaten
- Foutmeldingen verschijnen binnen 1 seconde en zijn zichtbaar in DOM.
- Screenreader leest foutmelding direct bij focus of via aria-live.
- Geen aria-hidden op essentiële form controls.
- Contrast en touch-targets voldoen aan WCAG AA/AAA waar nodig.
Test direct je pagina met onze WCAG checker/validator, download de plugin voor automatische scans en neem contact op via het contactformulier — we beantwoorden vragen binnen 24 uur.