Praktische checklist: WCAG-conformiteit stap voor stap

Toegankelijke formulieren & foutafhandeling — Praktische implementatie | WCAGTool

Toegankelijke formulieren & foutafhandeling: praktisch, testbaar en direct toepasbaar

Formulieren en foutafhandeling zijn in de praktijk een van de meest voorkomende WCAG-issues: ontbrekende labels, onzichtbare foutmeldingen voor screenreaders, slechte focusmanagement en custom controls die niet keyboard- of screenreader-vriendelijk zijn. Dat leidt tot uitval, supportverzoeken en compliance-risico’s.

Wij vertalen WCAG naar concrete code, testcases en checklists die developers, frontend engineers, designers en redacties direct kunnen gebruiken. Gebruik onze stappen, codevoorbeelden en testinstructies, of test meteen met de WCAG checker. Download onze plugin voor automatische checks: wcagtool.nl/plugin. Vragen? Gebruik het contactformulier — antwoord binnen 24 uur.

Het probleem in de praktijk

Veelvoorkomende fouten

  • Labels ontbreken of zijn niet gekoppeld (WCAG 3.3.2, 1.3.1).
  • Foutmeldingen zijn visueel, maar niet aangekondigd voor screenreaders (WCAG 3.3.1).
  • Geen focus op het eerste ongeldige veld na submit — keyboardgebruikers stapelen fouten op.
  • Custom controls missen role/keyboard handlers of zijn niet focusable (WCAG 4.1.2, 2.1.1).
  • Contrast van foutmeldingen en focus indicators is onvoldoende (WCAG 1.4.3/1.4.11).

Waarom het vaak misgaat

Designers en devs splitten responsibilities: visueel ontwerp eerst, accessibility later; backend en frontend validatie zijn niet gesynct; en er is weinig standaardisatie over IDs en aria-attributen in teams. Wij lossen dit op met herbruikbare patterns, testcases en automatisering via onze plugin en online checker.

Zo los je dit op in code

Basis: correct gekoppelde labels en required-indicators

Gebruik altijd <label for> gekoppeld aan een unieke id of wikkel input met label. Voeg een visuele en niet-visuele required-indicator toe.

<label for="email">E-mail<span class="sr-only"> (verplicht)</span></label><input id="email" name="email" type="email" required aria-required="true">

Foutmeldingen: aria-describedby en aria-invalid

Bij validatie: voeg een foutbericht toe met een id en koppel het met aria-describedby; zet aria-invalid=”true” op het veld. Gebruik role=”alert” of aria-live voor onmiddellijke aankondiging.

<input id="username" name="username" aria-describedby="username-error" aria-invalid="false"><div id="username-error" class="error" role="alert" aria-live="assertive"><!-- zichtbaar bij fout -->Vul uw gebruikersnaam in.</div>

Focus management: focus eerst het eerste ongeldige veld

Na client- of server-side validatie: verplaats focus naar het eerste foutveld en laat de fout horen via role=”alert”. Dit is cruciaal voor keyboard- en screenreadergebruikers.

// Minimal JS: focus first invalid fieldfunction focusFirstInvalid(form){const invalid = form.querySelector('[aria-invalid="true"], .error:visible');if(invalid){invalid.focus ? invalid.focus() : invalid.scrollIntoView();}}

Progressive enhancement: server-side + client-side consistentie

Valideer op server en reflecteer fouten in HTML die je direct koppelt met aria-describedby. Voeg JS alleen toe voor extra UX (live validation), nooit als enige toegangspunt.

<!-- server-rendered error example --><input id="phone" name="phone" value="<?php echo htmlentities($phone) ?>" aria-describedby="phone-error" aria-invalid="true"><div id="phone-error" role="alert">Ongeldig telefoonnummer</div>

Accessible custom controls (voorbeeld: custom checkbox)

Als je eigen UI-elemementen bouwt: bouw semantics na. Zorg voor role, tabindex, keyboard handlers en aria-checked.

<div role="checkbox" tabindex="0" aria-checked="false" id="agree" aria-label="Akkoord">Ik ga akkoord</div>// JS: togglefunction toggleCheckbox(el){const checked = el.getAttribute('aria-checked')==='true';el.setAttribute('aria-checked', String(!checked));}document.getElementById('agree').addEventListener('click', e => toggleCheckbox(e.currentTarget));document.getElementById('agree').addEventListener('keydown', e => {if(e.key===' '||e.key==='Enter'){e.preventDefault();toggleCheckbox(e.currentTarget);}});

CSS: zichtbare focus en foutstijl

Zorg dat focus zichtbaar is en foutmeldingen voldoende contrast hebben.

.focus-visible:focus{outline:3px solid #2563eb;outline-offset:2px;} .error{color:#b91c1c;background:#fff5f5;padding:.25rem;} .sr-only{position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden;}/* Zorg contrast-check via onze checker */

Checklist voor developers

  • Labels: elk form-element heeft een <label> gekoppeld of aria-label/aria-labelledby;
  • Errors: foutberichten hebben unieke ids en worden gekoppeld met aria-describedby;
  • aria-invalid wordt gezet bij fouten en gereset bij validatie-succes;
  • Focus: na submit focus op eerste foutveld; meld fout via role=”alert” of aria-live;
  • Keyboard: alle custom controls reageren op Space/Enter/Arrow keys en zijn tabbable;
  • Contrast: fouttekst en focus indicators voldoen aan WCAG-contrast (controleer met onze checker);
  • Server-rendered errors: render aria-attributen server-side zodat zonder JS nog steeds toegankelijk is;
  • Automatiseer checks met de WCAG plugin in CI pipelines.

Tips voor designers en redacties

Design tokens voor focus en foutstijl

Maak standaard tokens voor focus-outline, error-color en spacing. Test tegen kleurcontrasten en pas tokens toe in component library.

Schrijf korte, actiegerichte foutmeldingen

Foutmeldingen moeten vertellen wat fout is en hoe te herstellen: “Ongeldig e-mailadres. Gebruik bijvoorbeeld naam@domein.nl”. Voeg machine- en mensvriendelijke voorbeeldformaten toe.

Form-verdeling en progressive disclosure

Houd complexe formulieren in stappen; zorg dat elke stap een aria-live region heeft voor statusupdates en dat progressieve stappen keyboardvriendelijk zijn.

Redactiewerkflow

Maak een checklist voor content-editors: korte labels, geen placeholder-only labels, beschrijvingen in help-teksten gekoppeld via aria-describedby, en vaste formats (datum, telefoon) met voorbeelden.

Hoe test je dit?

Handmatige teststappen (snel)

  1. Tab-only navigatie: navigeer volledig via keyboard en check focus order en zichtbaarheid.
  2. Valideer foutafhandeling: laat een required veld leeg, submit en controleer focus en screenreader-aankondiging.
  3. Screenreader-test: gebruik NVDA (Windows) of VoiceOver (Mac) — controleer dat labels, foutberichten en aria-live aankondigingen correct zijn.
  4. Contrast-test: voer fouttekst en focus-indicatoren door onze WCAG checker.
  5. Automatische scans: run de plugin in je CI en fix regressies voor elke PR.

Testcases die je in je testplan opneemt

  • Submit met alle velden leeg: 1 foutmelding per veld en focus op eerste fout.
  • Invalid format: foutbericht duidelijk met voorbeeld; aria-describedby wijst naar fout.
  • Succesvolle submit: aria-live confirmatie of redirect zonder onzichtbare fouttraces.
  • Custom control keyboard: space/enter togglet en aria-checked klopt.

Automated scripts (voorbeeld Cypress)

// cypress voorbeeld: test focus op invalidit.cyit('focus op eerste invalid', ()=>{cy.visit('/formulier');cy.get('#email').clear();cy.get('form').submit();cy.focused().should('have.attr','id','email');cy.get('#email').should('have.attr','aria-invalid','true');});

Concrete mini-how-to’s

How-to: foutmelding koppelen met aria-describedby

Stappen:

  1. Geef foutmelding een uniek id: error-veldnaam.
  2. Zet aria-describedby op het input naar die id.
  3. Voeg role=”alert” of aria-live=”assertive” toe aan het bericht voor directe aankondiging.
<input id="postcode" aria-describedby="postcode-error"><div id="postcode-error" role="alert">Gebruik formaat 1234AB</div>

How-to: focus en scrol naar foutveld (plain JS)

// Plaats dit na validatie; roep focusFirstInvalid(form) aanfunction focusFirstInvalid(form){const first = form.querySelector('[aria-invalid="true"], .error');if(!first) return;const target = first.tagName.toLowerCase()==='div' ? document.getElementById(first.getAttribute('data-focus-target')) : first;target.focus({preventScroll:false});target.scrollIntoView({behavior:'smooth',block:'center'});}

How-to: server-rendered error pattern (PHP/Blade voorbeeld)

<input id="name" name="name" value="{{ old('name') }}" aria-describedby="{{ $errors->has('name') ? 'name-error' : '' }}" aria-invalid="{{ $errors->has('name') ? 'true' : 'false' }}">@if($errors->has('name'))<div id="name-error" role="alert">{{ $errors->first('name') }}</div>@endif

Extra resources en tools

  • Gebruik onze online WCAG checker om direct je pagina te scannen.
  • Installeer de WCAG plugin voor VS Code/CI integratie en voorkom regressies.
  • Vragen of quick audit? Gebruik het contactformulier — belofte: antwoord binnen 24 uur.

Laatste praktische tip

Plak deze kleine JS-functie in je formuliermodule: bij submit maakt hij fouten zichtbaar, zet aria-attributen en focust op het eerste ongeldige veld. Test direct door je site te scannen met onze checker en installeer de plugin voor geautomatiseerde checks.

// Complete helper: validate & focusfunction showErrors(form, errors){Object.keys(errors).forEach(name => {const input = form.querySelector('[name=\"'+name+'\"]');const id = name+'-error';let msg = document.getElementById(id);if(!msg){msg = document.createElement('div');msg.id = id;msg.className = 'error';msg.setAttribute('role','alert');input.insertAdjacentElement('afterend', msg);}msg.textContent = errors[name];input.setAttribute('aria-describedby', id);input.setAttribute('aria-invalid','true');});focusFirstInvalid(form);}/* Voorbeeld aanroepen: showErrors(document.querySelector('form'), {email:'Ongeldig e-mailadres'}); */

Previous Post Next Post

Geef een reactie

Je e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *