Wat is WCAG? Volledige inleiding en overzicht

Toegankelijke formulieren: praktische WCAG-implementatie | WCAGTool

Toegankelijke formulieren: labels, foutmeldingen & focusmanagement (praktische implementatie)

Formulieren zijn in de praktijk een van de grootste valkuilen voor WCAG-compliance: ontbrekende labels, onduidelijke foutmeldingen en slecht focusmanagement zorgen ervoor dat gebruikers niet kunnen invullen of corrigeren. Dat leidt tot drop-off, frustratie en toegankelijkheidsproblemen voor toetsenbord- en screenreadergebruikers.

Wij lossen dit op met kant-en-klare, testbare patterns: duidelijke label- en hintstructuren, ARIA-roles en properties voor foutafhandeling en een klein JavaScript-werkje om focus en aria-invalid automatisch te beheren. Test je pagina meteen met onze WCAG checker/validator, download onze plugin en neem contact op via ons contactformulier (reactie binnen 24 uur).

Het probleem in de praktijk

Veelgemaakte fouten in formulieren:

  • Labels niet gekoppeld aan inputs (geen for/id) of visueel verborgen zonder accessibility-aware technieken.
  • Foutmeldingen ontbreken of zijn niet gelinkt aan de input (geen aria-describedby/aria-invalid), screenreaders missen de context.
  • Geen focus op eerste fout, of focus wordt onjuist verplaatst waardoor toetsenbordgebruikers vastlopen.
  • Visuele aanwijzingen (kleur) zonder tekstuele ondersteuning; contrast en hover-only hints ontbreken.

Concrete gevolgen: toetsenbord- en screenreadergebruikers kunnen velden niet identificeren, foutmeldingen niet horen/lezen en de juiste actie niet ondernemen.

Zo los je dit op in code

Basismarkup: label, id, required en hint

Gebruik altijd expliciete labels en koppel hints via aria-describedby. Voor required velden gebruik zowel visuele markering als aria-required.

<form id="signup-form">
  <div class="form-row">
    <label for="email">E-mailadres <span aria-hidden="true" class="required">*

Foutmeldingen en ARIA

Gebruik aria-invalid op foutieve inputs en associeer fouttekst met aria-describedby. role="alert" en aria-live zorgen dat screenreaders de foutmelding voorlezen bij dynamische updates.

// bij validatie-failure (server of client)
input.setAttribute('aria-invalid', 'true');
errorElement.textContent = 'Voer een geldig e-mailadres in.';
errorElement.style.display = 'block'; // of class toevoegen
// errors gekoppeld via aria-describedby in de HTML

Focus op eerste fout: JavaScript snippet

Zodat toetsenbordgebruikers meteen weten waar ze moeten corrigeren.

function focusFirstError(form) {
  const firstInvalid = form.querySelector('[aria-invalid="true"], .error:not(:empty)');
  if (firstInvalid) {
    const target = firstInvalid.tagName === 'DIV' ? firstInvalid.previousElementSibling : firstInvalid;
    target.focus();
    // voor zichtbaarheid
    target.scrollIntoView({behavior: 'smooth', block: 'center'});
  }
}
// gebruik na validatie
focusFirstError(document.getElementById('signup-form'));

Server-side fouten integreren

Als je server validatie-errors terugstuurt, render dan foutdivs met id's en zet aria-invalid op de inputs. Voorvoorbeeld in een server-rendered template:

<input id="username" name="username" value="{{ username }}" aria-describedby="username-error" aria-invalid="{{ username_error ? 'true' : 'false' }}" />
<div id="username-error" class="error" role="alert">{{ username_error }}</div>

Stijl: focus-visible en contrastrijke foutstijl

Gebruik :focus-visible voor duidelijke toetsenbordfocus en zorg dat foutstates voldoende contrast hebben.

input:focus-visible { outline: 3px solid #005fcc; outline-offset: 2px; }
.error { color: #b00020; background: #fff0f0; padding: 6px; border-radius: 3px; }
.required { color: #b00020; margin-left: 4px; }

Accessible custom UI components (datepickers, dropdowns)

Als je custom controls bouwt, implementeer ARIA roles en keyboard support. Voor een custom dropdown:

<div role="combobox" aria-expanded="false" aria-owns="listbox-1" aria-haspopup="listbox">
  <input aria-autocomplete="list" aria-controls="listbox-1" />
  <ul id="listbox-1" role="listbox"><li role="option">Optie 1</li></ul>
</div>

Checklist voor developers

  • Elk input-element heeft een gekoppeld <label for="..."></label> met unieke id op het input.
  • Hints en foutmeldingen zijn gekoppeld via aria-describedby en bevatten unieke id's.
  • Bij fout: input heeft aria-invalid="true" en foutdiv heeft role="alert" en aria-live (assertive voor kritieke fouten).
  • Toetsenbord: tab-order logisch, :focus-visible styling aanwezig, custom components ondersteunen Enter/Arrow/Space en aria-roles.
  • Visuele cues niet alleen kleur: voeg iconen/tekst of extra aria-describedby toe.
  • Server-side en client-side validatie consistent; errors renderen met id's zodat screenreaders ze oppikken.
  • Gebruik onze WCAG checker om per pagina te scannen en onze plugin voor integratie met CI/CD.

Tips voor designers en redacties

Formulierstructuur en content

Schrijf korte, duidelijke labels en hints. Plaats labels boven het veld (verticale layout) voor betere scanbaarheid. Gebruik actieve taal in foutmeldingen en vermeld wat te doen: "Voer een geldig e-mailadres in" i.p.v. "Ongeldig e-mailadres".

Visuele feedback

Zorg dat foutstates en required markers voldoen aan contrastrichtlijnen (WCAG AA). Voeg iconen en tekstuele uitleg toe; rely nooit alleen op kleur.

UX: minimaliseer fouten

Gebruik inline validatie terwijl de gebruiker typt (met voorzichtigheid: niet te assertief). Toon voorbeelden in placeholders of hints (bijv. "voorbeeld@domein.nl"). Gebruik progressieve onthulling voor complexe formulieren en valideer stap-voor-stap.

Hoe test je dit?

Handmatige tests (snel en effectief)

  1. Toetsenbord-only: navigeer met Tab, Shift+Tab, Enter en pijltjestoetsen. Kun je alle velden bereiken en activeren?
  2. Screenreader: test met NVDA (Windows) en VoiceOver (macOS/iOS). Controleer of labels, hints en foutmeldingen worden aangekondigd.
  3. Foutscenario's: forceer client- en serverfouten. Zorg dat aria-invalid en role="alert" de foutmelding voorlezen en dat focus naar de eerste fout gaat.
  4. Contrast: controleer met contrast-checker of foutteksten en focus-outlines voldoen aan WCAG AA (minimaal 3:1 voor UI-componenten, 4.5:1 voor tekst).

Automatische tests

Integreer onze plugin in CI zodat elke build gescand wordt. Gebruik ook axe-core (npm) in unit of e2e tests:

const { AxePuppeteer } = require('axe-puppeteer');
const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://jouw-site.example/form');
  const results = await new AxePuppeteer(page).analyze();
  console.log(results.violations);
  await browser.close();
})();

Concrete teststappen lijst

  1. Open formulier en verwijder alle cookies zodat server-side errors getest kunnen worden.
  2. Laat leeg velden achter en verzend — controleer aria-invalid en role="alert".
  3. Typ een ongeldige waarde (bv. "test" in e-mail) — controleer inline validatie en aria-describedby.
  4. Navigeer met toetsenbord: kan je foutmelding bereiken en focus terug naar input?
  5. Gebruik screenreader: wordt label, hint en foutmelding in de juiste volgorde aangekondigd?
  6. Voer contrast- en color-blindness-check uit en scan met onze WCAG checker.

Extra mini-how-to's

Snel pattern: required field met duidelijke foutmelding (complete snippet)

<form id="contact">
  <label for="phone">Telefoonnummer <span class="required" aria-hidden="true">*</span></label>
  <input id="phone" name="phone" pattern="^\d{10}$" aria-describedby="phone-hint phone-error" aria-required="true" />
  <div id="phone-hint" class="hint">10 cijfers, zonder spaties.</div>
  <div id="phone-error" class="error" role="alert" aria-live="assertive"></div>
  <button type="submit">Verstuur</button>
</form>
<script>
document.getElementById('contact').addEventListener('submit', function(e){
  e.preventDefault();
  const phone = document.getElementById('phone');
  const err = document.getElementById('phone-error');
  err.textContent = '';
  phone.removeAttribute('aria-invalid');
  if(!/^\d{10}$/.test(phone.value)){
    phone.setAttribute('aria-invalid','true');
    err.textContent = 'Voer een geldig 10-cijferig telefoonnummer in (zonder spaties).';
    phone.focus();
  } else {
    // submit via fetch / normale submit
  }
});
</script>

Minimal inline-validatie zonder overlast

Gebruik aria-live="polite" voor niet-kritische hints en "assertive" voor foutmeldingen die direct actie vereisen.

Calls-to-action en tools

Test deze pagina direct met onze WCAG checker/validator en krijg een lijst met concrete fouten en codevoorstellen. Download onze plugin voor automatische scans in je CI. Heb je een vraag of wil je hulp bij implementatie? Gebruik ons contactformulier — wij reageren binnen 24 uur.

Wil je dat we je formulier live reviewen? Kopieer de URL naar onze checker en deel de resultaten met ons via het contactformulier.

Laatste praktische tip: voeg dit korte script toe aan alle formulieren om automatisch aria-describedby te koppelen aan foutdivs met patroon-id's (paste in je globale JS):

(function(){
  document.addEventListener('submit', function(e){
    const form = e.target;
    if(!form || form.tagName !== 'FORM') return;
    setTimeout(()=>{ // allow server-rendered errors to appear
      form.querySelectorAll('.error').forEach(function(err){
        if(err.id){
          const input = form.querySelector('[aria-describedby*="'+err.id+'"], #'+err.id);
          // Als geen expliciete koppeling, probeer inputs in dezelfde .form-row
          if(!form.querySelector('[aria-describedby*="'+err.id+'"]')){
            const row = err.closest('.form-row');
            if(row){
              const inputInRow = row.querySelector('input, textarea, select');
              if(inputInRow){
                let desc = inputInRow.getAttribute('aria-describedby') || '';
                if(!desc.includes(err.id)) {
                  desc = (desc + ' ' + err.id).trim();
                  inputInRow.setAttribute('aria-describedby', desc);
                }
                inputInRow.setAttribute('aria-invalid','true');
                inputInRow.focus();
              }
            }
          }
        }
      });
    }, 50);
  }, true);
})();

Test je implementatie direct met onze WCAG checker, installeer de plugin voor continue monitoring en vraag ondersteuning via ons contactformulier (antwoord binnen 24 uur).

Previous Post Next Post

Geef een reactie

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