Toegankelijke formulieren: labels, validatie, focus en ARIA
Formulieren zijn een van de meest geopende en foutgevoelige onderdelen van websites en apps: ontbrekende labels, onduidelijke foutmeldingen en slecht focusbeheer zorgen snel voor ontoegankelijkheid. Developers en designers denken vaak “dat werkt wel”, maar schermlezers en toetsenbordgebruikers lopen meteen vast.
Wij laten precies zien hoe je dit praktisch oplost: kant-en-klare HTML/ARIA/CSS/JavaScript snippets, testbare stappen en checklists. Test je pagina direct met onze WCAG checker, download onze validator-plugin op wcagtool.nl/plugin en vraag ons om hulp via het contactformulier (vragen worden binnen 24 uur beantwoord).
Het probleem in de praktijk
Veelvoorkomende fouten die we zien:
- Labels ontbreken of zitten niet in een
<label for="id">
-relatie. - Foutmeldingen zijn visueel, maar niet voor schermlezers (geen
aria-describedby
ofrole="alert"
). - Geen focus naar het eerste foutveld na validatie.
- Custom widgets (selects, radio groups) hebben geen keyboard-ondersteuning of ARIA-roles.
Waarom dit vaak misgaat
Designers leveren visuele comps; developers implementeren visueel correcte maar semantisch lege elementen; content-editors veranderen labels zonder IDs bij te werken. De oplossing is proces + concrete code.
Zo los je dit op in code
Basis: semantische HTML en labels
Gebruik altijd native form controls met een gekoppeld label. Voorbeeld (copy-paste):
<form id="contact" novalidate>
<div class="field">
<label for="name">Naam</label>
<input id="name" name="name" type="text" required aria-required="true" />
</div>
<div class="field">
<label for="email">E-mail</label>
<input id="email" name="email" type="email" required aria-required="true" />
</div>
<button type="submit">Verstuur</button>
</form>
Inline foutmeldingen met ARIA
Bind foutboodschappen aan velden via aria-describedby
en zet aria-invalid
op het invoerveld. Gebruik role="alert"
of aria-live="assertive"
zodat schermlezers direct voorlezen.
<div class="field">
<label for="email">E-mail</label>
<input id="email" name="email" type="email" aria-describedby="email-error" />
<div id="email-error" class="error" role="alert" aria-live="assertive" aria-atomic="true">Voer een geldig e-mailadres in.</div>
</div>
JavaScript: valideren en focus management
Valideer op submit, toon foutmeldingen, zet aria-invalid
en focus op het eerste foutveld.
document.getElementById('contact').addEventListener('submit', function(e){
e.preventDefault();
const form = e.target;
const fields = Array.from(form.querySelectorAll('input,textarea,select'));
let firstInvalid = null;
fields.forEach(f => {
const errorId = f.id + '-error';
let errorEl = document.getElementById(errorId);
const valid = f.checkValidity();
if(!valid){
f.setAttribute('aria-invalid','true');
if(!errorEl){
errorEl = document.createElement('div');
errorEl.id = errorId; errorEl.className = 'error'; errorEl.setAttribute('role','alert'); errorEl.setAttribute('aria-live','assertive');
f.insertAdjacentElement('afterend', errorEl);
}
errorEl.textContent = f.validationMessage || 'Ongeldige invoer.';
if(!firstInvalid) firstInvalid = f;
} else {
f.removeAttribute('aria-invalid');
if(errorEl) errorEl.remove();
}
});
if(firstInvalid){
firstInvalid.focus();
firstInvalid.scrollIntoView({behavior:'smooth',block:'center'});
return; // stop submit
}
// formulier is geldig: voer ajax uit of submitten
form.submit();
});
CSS: zichtbare en duidelijke focus states
Gebruik focus-styling die zichtbaar is voor toetsgebruikers zonder layout te breken.
:focus{outline:3px solid #005a9c;outline-offset:2px}/* of gebruik :focus-visible voor meer nuance */
Toegankelijke custom select / dropdown (minimal example)
Als je native select niet kan gebruiken: implementeer keyboard en ARIA (arrow keys, enter, esc).
<div class="combobox" role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-owns="listbox-1" tabindex="0" id="combo-1">Kies een optie</div>
<ul role="listbox" id="listbox-1" tabindex="-1" hidden>
<li role="option" data-value="1">Optie 1</li>
<li role="option" data-value="2">Optie 2</li>
</ul>
/* JS: beheer aria-expanded, focus op options, keyboard: ArrowDown/Up, Enter, Esc */
Checklist voor developers
- Zorg dat elk input-element een gekoppeld <label for="id"> heeft.
- Gebruik input types (email, tel, url, number) voor betere mobiel- en validatie-ondersteuning.
- Toon inline foutmeldingen met een uniek ID en koppel via
aria-describedby
. - Zet
aria-invalid="true"
op foutvelden en update deze dynamisch. - Vernietig of update fout-elementen bij succesvolle correctie.
- Focus altijd het eerste invalid-veld op submit en announce de fout via
role="alert"
ofaria-live
. - Voor custom widgets: implementeer ARIA-roles (combobox, option, listbox, radiogroup), keyboard-navigatie en focusbeheer.
- Test met alleen toetsenbord, met schermlezer (NVDA/VoiceOver) en met onze WCAG checker.
Tips voor designers en redacties
Designers: ruimte en labels
- Voorzie altijd ruimte voor foutmeldingen in het design zodat content niet verschuift en focus zichtbaar blijft.
- Maak labels kort maar beschrijvend; geef voorbeelden of placeholders niet als vervanging van labels.
Redacties: taal en foutteksten
- Schrijf foutmeldingen als actiegerichte instructies: vermijd jargon, geef het verwachte formaat (bv. "Gebruik het formaat +31 6 12345678").
- Houd teksten kort en test of ze voorgelezen duidelijk zijn in een schermlezer.
Wil je dat wij je content-reviewen? Upload je pagina naar onze WCAG checker of gebruik de plugin: download plugin. Vragen? Gebruik onze contactpagina — we reageren binnen 24 uur.
Hoe test je dit?
Handmatige testcases
- Verwijder of verander labeltext en controleer of het schermlezer-account nog steeds de juiste label-tekst leest.
- Probeer formulier te verzenden zonder verplichte velden en controleer dat: foutmelding zichtbaar is, gekoppeld via
aria-describedby
,aria-invalid
op veld staat en focus op eerste foutveld gaat. - Gebruik alleen toetsenbord: tab, shift+tab, enter en escape; controleer logische tabvolgorde en zichtbare focus.
- Voer fout in en corrigeer; controleer dat foutmeldingen verdwijnen en
aria-invalid
wordt verwijderd. - Test custom widgets met arrow keys en Enter; controleer roles en states met Accessibility tree (browser devtools).
Automatische en tool-ondersteunde tests
- Draai onze WCAG checker om snel structurele issues te vinden.
- Gebruik browser Accessibility pane (Chrome/Edge/Firefox) om ARIA attributes en accessible name te inspecteren.
- Integreer linting in CI met plugins die HTML-validiteit en ARIA-contradictions detecteren.
Screenreader quick-check
- Schakel NVDA of VoiceOver in.
- Navigeer naar het formulier: lees labels, navigeer naar elk veld en controleer of labels/placeholder/desc voorgelezen worden.
- Voer verkeerde waarden in, submit en luister of de foutmelding direct aangekondigd wordt (role=alert / aria-live).
Direct testen? Zet je URL in onze WCAG checker of installeer onze validator-plugin: download. Neem contact op via contact voor een snelle review (antwoord binnen 24 uur).
Laatste praktische tip
Implementatie-check in één snippet: voeg deze functie toe aan je project en voer hem uit bij build of CI om ontbrekende label-for relaties en inputs zonder id te vinden (paste in je console of CI-job):
(function(){const forms = document.querySelectorAll('form');let issues = [];forms.forEach(form => {const inputs = form.querySelectorAll('input,textarea,select');inputs.forEach(i => {if(!i.id){issues.push('Missing id on '+(i.name||i.type||i.tagName));}else{const label = document.querySelector('label[for="'+i.id+'"]');if(!label){issues.push('No label for input id='+i.id);}}});});if(issues.length){console.warn('Accessibility quick-check found issues:',issues);console.table(issues);}else{console.log('Quick-check: labels and ids ok. Run full scan with https://wcagtool.nl');}})();
Gebruik deze check, test je site direct met onze WCAG checker, download de plugin op wcagtool.nl/plugin en stuur vragen via ons contactformulier — we helpen binnen 24 uur.