Toegankelijke formulieren en foutmeldingen
Formulieren zijn in de praktijk één van de grootste pijnpunten voor WCAG-compliance: labels ontbreken of zijn onduidelijk, foutmeldingen worden visueel weergegeven maar niet aan assistive tech gemeld, en keyboard- of focusflow faalt bij dynamische validatie. Dat zorgt voor uitval, frustratie en juridische risico’s.
Wij helpen developers, designers en redacties met concrete, stap-voor-stap oplossingen: semantische HTML, correcte ARIA-attributen, focusmanagement en testbare JavaScript-patronen. Gebruik direct de voorbeelden en test je site met onze WCAG checker/validator of installeer onze plugin — vragen? Contact via het contactformulier, reactie binnen 24 uur.
Het probleem in de praktijk
Veelvoorkomende fouten
- Geen of onjuiste
<label>
koppeling met invoervelden. - Visuele foutmeldingen zonder ARIA-live of focus naar fout.
- Gebruik van placeholders als vervanging van labels.
- Onvoldoende contrast van fout- of helptekst.
- Dynamische validatie zonder passende roles (
aria-invalid
,aria-describedby
,role="alert"
).
Waarom dat fout gaat
Veel teams sturen op visuele mockups en denken dat client-side scripts alles “fixen”, maar screenreader-gebruikers en keyboard-only users missen de semantiek. Ook ontbreken vaak regressietests waarmee deze cases automatisch falen.
Zo los je dit op in code
1) Basisformulieren: semantische HTML en labels
<form id="signupForm"><br> <div class="form-row"><br> <label for="email">E-mailadres</label><br> <input id="email" name="email" type="email" required aria-describedby="email-help email-error"><br> <div id="email-help" class="help">We sturen geen spam.</div><br> <div id="email-error" class="error" aria-live="assertive"></div><br> </div><br> <button type="submit">Aanmelden</button><br></form>
Uitleg
Gebruik vaste id’s en verwijs met for
en aria-describedby
. Error-elementen krijgen aria-live="assertive"
zodat screenreaders wijzigingen direct aankondigen.
2) Client-side validatie: aria-invalid en focusmanagement
document.getElementById('signupForm').addEventListener('submit', function(e){<br> e.preventDefault();<br> const email = document.getElementById('email');<br> const emailError = document.getElementById('email-error');<br> email.removeAttribute('aria-invalid');<br> emailError.textContent = '';<br> if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {<br> email.setAttribute('aria-invalid','true');<br> emailError.textContent = 'Vul een geldig e‑mailadres in.';<br> emailError.classList.add('visible');<br> email.focus(); // focus naar het foutveld<br> return; // stop verzending<br> }<br> // verstuur AJAX of submit<br>});
Uitleg
Zet aria-invalid="true"
op het input als de waarde niet klopt, update de error-container (die aria-live
heeft) en breng focus naar het relevante veld — belangrijk voor keyboard- en screenreader-gebruikers.
3) Complexe formulieren en meerdere foutmeldingen
// Voor meerdere fouten: maak een overzicht en link naar velden via id's<br>function showErrors(errorMap){<br> // errorMap = { fieldId: 'Foutmelding' }<br> const summary = document.getElementById('form-error-summary');<br> summary.innerHTML = '<h2>Er zijn fouten in het formulier</h2><ul>';<br> Object.keys(errorMap).forEach(id => {<br> const msg = errorMap[id];<br> const li = document.createElement('li');<br> const a = document.createElement('a');<br> a.href = `#${id}`;<br> a.textContent = msg;<br> a.addEventListener('click', function(e){ e.preventDefault(); document.getElementById(id).focus(); });<br> li.appendChild(a);<br> summary.querySelector('ul').appendChild(li);<br> document.getElementById(id).setAttribute('aria-invalid','true');<br> const errEl = document.getElementById(id + '-error'); errEl.textContent = msg;<br> });<br> summary.setAttribute('tabindex','-1'); summary.focus(); // focus naar samenvatting<br>}
Uitleg
Een foutensamenvatting bovenaan het formulier met links naar de velden verbetert navigatie; zet tabindex -1 en focus naar de samenvatting zodat schermlezers en keyboard-gebruikers direct de problemen weten.
4) CSS: zichtbare focus en contrast
.form-row input:focus { outline: 3px solid #0b66ff; outline-offset: 2px; } .error { color: #b00020; } .error.visible { display: block; } /* Verberg fouten niet visueel voor screenreaders */ .error[aria-hidden="true"] { display:none; }
Uitleg
Zorg dat focus duidelijk zichtbaar is (niet alleen box-shadow die wegvalt bij hoge-contrastinstellingen) en dat foutkleurcontrast voldoet aan WCAG AA/AAA afhankelijk van tekstgrootte.
Checklist voor developers
- Alle inputs hebben een gekoppeld <label> of aria-label/aria-labelledby.
- Gebruik
required
en type-attributen (email, tel, url) naast JS-validatie. - Foutberichten hebben een id en worden genoemd in
aria-describedby
. - Bij fouten: zet
aria-invalid="true"
op het invoerveld; verwijder het wanneer valide. - Bij directe foutmelding wijziging:
aria-live="assertive"
ofrole="alert"
gebruiken. - Toon een foutensamenvatting met links naar velden en focus ernaartoe.
- Zorg voor zichtbare focusstijlen en voldoende kleurcontrast.
- Automatiseer tests (axe-core, pa11y) en gebruik onze WCAG checker/validator bij elke CI-run.
Tips voor designers en redacties
Designers — componentrichtlijnen
- Maak formuliercomponenten met vaste plekken voor help- en error-teksten (altijd ruimte reserveren om layout-shift te voorkomen).
- Ontwerp foutensamenvatting en individuele fouten als klikbare links naar velden.
- Documenteer focusstijlen in design-system — gebruik niet alleen kleur om status te communiceren.
Redacties — contentrichtlijnen
- Schrijf korte, concrete foutmeldingen: wat is fout en hoe los je het op. Vermijd technische termen.
- Gebruik actieve taal en voorbeeldvalidatie: “Gebruik dit formaat: 0612345678”.
- Houd helptekst bij invoer beknopt en nuttig; zet uitgebreide instructies in een apart help-venster met aria-controls/aria-expanded als nodig.
Hoe test je dit?
Handmatige stappen
- Keyboard-only: navigeer naar elk veld, vul foutieve data in, verstuur en controleer of focus en samenvatting logisch zijn.
- Screenreader (NVDA/VoiceOver): lees het formulier, voer fout in, controleer dat foutmelding wordt aangekondigd en dat de samenvatting wordt voorgelezen.
- Contrast: controleer fout- en hulptekst met contrastchecker (WCAG AA minimaal 4.5:1 voor normale tekst).
- Responsief: test op mobiel en tablet; zorg dat foutensamenvatting niet buiten viewport valt zonder mogelijkheid om ernaartoe te navigeren.
Automatische tests
Integreer deze checks in CI met tools zoals axe-core en pa11y. Voorbeeld npm-script:
"scripts": { "test:a11y": "pa11y-ci --config ./pa11yci.js" }
Gebruik daarnaast onze WCAG checker/validator op wcagtool.nl of installeer onze browser-plugin voor snelle lokale scans. Draai scans na elke release.
Verifieer met concrete testcases
- Case: leeg required veld — verwacht: focus naar veld, aria-invalid, samenvatting met link.
- Case: meerdere fouten — verwacht: samenvatting met één lijst, elke link verplaatst focus naar correct veld.
- Case: dynamische validatie terwijl gebruiker typt — verwacht: aria-live-updates, geen break van keyboardflow.
Run deze cases handmatig en als geautomatiseerde e2e-tests (Cypress + axe plugin). Vergeet niet om onze WCAG checker/validator in te zetten en de plugin te downloaden — test direct je site op wcagtool.nl.
Vraag hulp of stuur voorbeelden van je implementatie via ons contactformulier; we beantwoorden binnen 24 uur en geven concrete code-aanpassingen.
Laatste praktische tip: voeg dit kleine helper-snippet toe in je project om bij submit alle error-containers te resetten en focus consistent te beheren:
function resetFormErrors(form){const errors = form.querySelectorAll('.error');errors.forEach(e => { e.textContent=''; e.classList.remove('visible'); });const invalids = form.querySelectorAll('[aria-invalid="true"]');invalids.forEach(i => i.removeAttribute('aria-invalid'));}
Test je implementatie meteen met onze online WCAG checker/validator op wcagtool.nl, of installeer onze plugin voor snelle lokale scans. Heb je vragen? Gebruik het contactformulier — we reageren binnen 24 uur.