Toegankelijke formulieren: praktische implementatie van WCAG in HTML, ARIA en JavaScript
Formulieren falen in de praktijk vaak door ontbrekende labels, onduidelijke foutmeldingen, slechte focusmanagement en verkeerd gebruik van ARIA. Dit leidt tot afgehaakte gebruikers, frustratie bij schermlezergebruikers en mislukte conversies.
Wij lossen dit op met concrete, testbare patterns: complete HTML-structuren, bijpassende CSS voor zichtbare focus- en foutstates, en JavaScript-voorbeelden voor inline validatie en correcte ARIA-updates. Test direct met onze WCAG checker, probeer de plugin en stel vragen via het contactformulier (antwoord binnen 24 uur).
Het probleem in de praktijk
Veelvoorkomende fouten
- Labels ontbreken of worden als placeholder gebruikt in plaats van echte
<label>
-elementen. - Foutmeldingen zijn niet gekoppeld aan velden (geen
aria-describedby
) en worden niet aangekondigd door schermlezers. - Focus springt niet naar het foutveld; keyboard gebruikers weten niet waar ze naartoe moeten.
- Visuele focus ontbreekt of is niet contrastrijk, waardoor toetsenbordgebruikers verdwalen.
- ARIA-attributen worden verkeerd gebruikt (bijv.
aria-hidden
op interactieve elementen).
Zo los je dit op in code
Basis HTML-structuur: semantisch en screenreader-vriendelijk
Gebruik altijd echte labels, voeg helpertekst en foutmeldingen toe met ID’s en koppel ze via aria-describedby
. Voor groepsvelden gebruik <fieldset>
en <legend>
.
<form id="signup-form" novalidate><div class="form-row"><label for="email">E-mailadres</label><input id="email" name="email" type="email" required aria-describedby="email-help email-error" /><small id="email-help">We sturen een bevestiging naar dit adres.</small><div id="email-error" class="error" aria-live="polite"></div></div><div class="form-row"><label for="password">Wachtwoord</label><input id="password" name="password" type="password" minlength="8" required aria-describedby="password-help password-error" /><small id="password-help">Minimaal 8 tekens.</small><div id="password-error" class="error" aria-live="polite"></div></div><button type="submit">Aanmelden</button></form>
CSS: zichtbare focus en foutstijlen
Gebruik focus-visible voor toetsenbordgebruikers en contrastrijke foutstates.
.form-row input:focus{outline:3px solid #005fcc;outline-offset:2px;} .error{color:#a00000;margin-top:4px;} input[aria-invalid="true"]{border:2px solid #a00000;box-shadow:0 0 0 3px rgba(160,0,0,0.1);}
JavaScript: inline validatie en ARIA-updates (testbaar)
Voorkom default browsermeldingen, valideren on submit en live, zet aria-invalid
en werk aria-describedby
bij. Announce fouten via aria-live
of focus naar het eerste foutveld.
document.getElementById('signup-form').addEventListener('submit',function(e){e.preventDefault();const form=this;let firstError=null;clearErrors(form);const email=form.querySelector('#email');const password=form.querySelector('#password');if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)){setError(email,'Voer een geldig e-mailadres in');firstError=firstError||email;}if(password.value.length<8){setError(password,'Wachtwoord moet minstens 8 tekens zijn');firstError=firstError||password;}if(firstError){firstError.focus();return;}submitForm(form);});function setError(input,message){const err=document.getElementById(input.id+'-error');input.setAttribute('aria-invalid','true');err.textContent=message;err.setAttribute('role','alert');}function clearErrors(form){form.querySelectorAll('[aria-invalid="true"]').forEach(i=>i.removeAttribute('aria-invalid'));form.querySelectorAll('.error').forEach(e=>{e.textContent='';e.removeAttribute('role');});}function submitForm(form){ /* echte submit logica hier */ alert('Formulier klaar om te verzenden');}
Checklist voor developers
- Zorg voor een
<label for>
per input; geen placeholder-only labels. - Koppel helpertekst en foutmeldingen met unieke ID’s en
aria-describedby
. - Gebruik
aria-live="polite"
ofrole="alert"
voor dynamische foutmeldingen. - Zet
aria-invalid="true"
op velden met fouten. - Breng keyboard-focus naar het eerste foutveld (
element.focus()
). - Voorkom inline placeholders als vervanging van labels; ze verdwijnen bij input.
- Test met toetsenbord, NVDA/VoiceOver en onze WCAG checker.
Tips voor designers en redacties
Design: focus en contrast
- Ontwerp een duidelijke focusstate: minimaal 3px zichtbare rand of contrastieve schaduw.
- Houd contrast van foutsteken en labels minimaal AA (4.5:1 voor tekst; 3:1 voor grote tekst).
- Zorg voor voldoende ruimte tussen label, input en foutmelding.
Redactie: taal en helptekst
- Schrijf korte, actiegerichte foutmeldingen die aangeven wat fout is en hoe te herstellen (bijv. “Wachtwoord te kort — voeg 2 tekens toe”).
- Vermijd visuele hints als enige instructie; leg belangrijke instructies in zichtbare helpertekst met eigen ID gekoppeld aan het veld.
- Gebruik duidelijke vermeldingen van verplichte velden (bijv. label + “verplicht” of aparte tekst, niet alleen kleur).
Hoe test je dit?
Automatisch en handmatig combineren
Begin met een automatische scan (onze WCAG checker). Gebruik daarnaast keyboard- en screenreader-tests voor echte gebruikersscenario’s.
Stappen voor handmatige tests
- Keyboard-only: navigeer naar elk formulier met Tab, activeer knoppen met Enter/Space, en controleer dat focus logisch is en zichtbaar blijft.
- Foutinjectie: submit met lege/ongeldige waarden en controleer dat foutmeldingen zichtbaar zijn, gekoppeld aan velden (
aria-describedby
) en worden aangekondigd. - Screenreader: gebruik NVDA (Windows) of VoiceOver (macOS/iOS) en controleer label- en foutmelding-announce; gebruik rotor/landmarks om naar formulieren te springen.
- Color contrast: meet labels, helptekst en foutteksten met een contrasttool; streef naar WCAG AA of hoger.
- Responsiveness: test op mobiel; focus en foutmeldingen mogen niet buiten scherm vallen zonder scrollen.
- Gebruik onze WCAG checker en installeer de plugin voor CI-integratie en snelle feedback.
Testcases die je direct kunt uitvoeren
- Voer formulier in en druk op submit zonder waarden — controleer of de eerste foutveld focus krijgt.
- Schakel CSS uit en controleer of labels nog begrijpelijk zijn (progressieve verfijning).
- Gebruik schermleesmodus en controleer of helpertekst en foutmelding worden voorgelezen.
Praktische how-to’s & snippets
How-to: focus naar eerste foutveld (kopieer/klaar)
function focusFirstError(form){const field=form.querySelector('[aria-invalid="true"], .error:empty ~ input');if(field){field.focus();field.scrollIntoView({block:'center'});} }
How-to: meertalige foutmeldingen (voorbeeld)
const messages={en:{email:'Enter a valid email'},nl:{email:'Voer een geldig e-mailadres in'}};function getMessage(code,lang){return messages[lang] && messages[lang][code] ? messages[lang][code] : messages['en'][code];}
Checklist om direct te implementeren
- Voeg labels en
aria-describedby
toe aan elk inputveld. - Implementeer
aria-live
ofrole="alert"
voor foutmeldingen. - Maak focus-management: focus naar eerste fout en gebruik
:focus
/:focus-visible
CSS. - Verifieer met keyboard, screenreader en onze WCAG checker.
- Integreer snelle checks in CI met onze plugin.
Wil je direct je site testen? Gebruik de WCAG checker op wcagtool.nl, installeer de plugin voor automatische scans en stuur vragen via het contactformulier — we antwoorden binnen 24 uur.
Laatste praktische tip: kopieer onderstaande one-liner in je project om bij submit automatisch focus naar het eerste foutveld te sturen en daarmee keyboard- en screenreadergebruikers direct te helpen.
document.getElementById('signup-form').addEventListener('submit',function(e){setTimeout(()=>{const f=this.querySelector('[aria-invalid="true"]');if(f){f.focus();f.scrollIntoView({block:'center'});}},0);});