De beste WCAG-testtools op een rij

Keyboard en focusbeheer: praktische WCAG-implementatie | wcagtool.nl – Praktische Implementatie

Keyboard en focusbeheer: praktische WCAG-implementatie

Keyboardtoegankelijkheid en correct focusbeheer gaan in de praktijk vaak fout: interactieve elementen missen focusstijlen, modals blokkeren of breken tabvolgorde, custom controls reageren niet op Enter/Space en editors vergeten focus bij fouten. Dit leidt tot gebrekkige bruikbaarheid voor toetsenbordgebruikers en faalt vaak WCAG 2.1 AA-eisen.

Wij helpen met concrete, testbare oplossingen: heldere HTML/ARIA-structuren, CSS voor zichtbare focus, JavaScript-patronen voor focus trapping en roving tabindex, plus checklists en teststappen. Test je site direct met onze WCAG checker, download onze plugin op wcagtool.nl/plugin en stel vragen via het contactformulier — we beantwoorden binnen 24 uur.

Het probleem in de praktijk

Veelvoorkomende foutbeelden

1) Geen focus-stijl of focus outline weggefilterd; 2) Custom knoppen/divs zonder keyboard handlers; 3) Modal dialogs zonder focus trap en zonder aria-hidden op achterliggende content; 4) Onlogische tabvolgorde door DOM-orde of verkeerd gebruik van tabindex; 5) Foutmeldingen die niet automatisch focus krijgen of niet programmatically focusable zijn.

Waarom dat faalt volgens WCAG

WCAG vereist keyboard-toegang (2.1.1) en duidelijke focusindicatoren (2.4.7), plus toegang tot content en bediening. Onjuiste implementatie verhindert toetsenbordgebruik en screenreader-navigatie.

Zo los je dit op in code

Basisregels

Gebruik semantische HTML waar mogelijk (<button>, <a>, <input>). Voeg tabindex alleen toe om interactieve volgorde te corrigeren (bij voorkeur tabindex=”0″ voor programmatic focus), vermijd tabindex=”1..n”. Gebruik aria-* alleen als semantiek met HTML niet mogelijk is.

Focus zichtbaar maken — CSS

.sr-focus-reset{outline:none;}.focus-visible{box-shadow:0 0 0 3px rgba(21,156,228,0.6);border-radius:4px;}button:focus, a:focus, input:focus{outline:none;}button:focus-visible, a:focus-visible, input:focus-visible{box-shadow:0 0 0 3px rgba(21,156,228,0.6);}

Gebruik :focus-visible voor moderne browsers en polyfill waar nodig. Dit voorkomt dat focus-styles verdwijnen op muisinteracties.

Skip link (eenvoudig, altijd toepassen)

<a href="#main-content" class="skip-link">Sla naar inhoud</a><style>.skip-link{position:absolute;left:-999px;top:auto;width:1px;height:1px;overflow:hidden}.skip-link:focus{position:static;left:auto;width:auto;height:auto;padding:8px;background:#fff;color:#000;z-index:9999}</style>

Toegankelijke custom controls — voorbeeld: klikbare DIV als knop

<div role="button" tabindex="0" aria-pressed="false" class="custom-btn">Lees meer</div><script>const btn=document.querySelector('.custom-btn');btn.addEventListener('click',()=>toggle());btn.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();toggle();}});function toggle(){const state=btn.getAttribute('aria-pressed')==='true';btn.setAttribute('aria-pressed',String(!state));}</script>

Modal met focus trap en aria-hidden op achtergrond

<!-- Trigger --><button id="openModal">Open modal</button><div id="page" aria-hidden="false">...rest van pagina...</div><div id="modal" role="dialog" aria-modal="true" aria-hidden="true" style="display:none"><button id="closeModal">Close</button><div tabindex="0">Eerste focus</div><button>Actie</button></div><script>const open=document.getElementById('openModal');const modal=document.getElementById('modal');const page=document.getElementById('page');const close=document.getElementById('closeModal');let focusable=null;let first=null;let last=null;open.addEventListener('click',()=>{page.setAttribute('aria-hidden','true');modal.style.display='block';modal.setAttribute('aria-hidden','false');focusable=modal.querySelectorAll('button,[href],input,select,textarea,[tabindex]:not([tabindex=\"-1\"])');first=focusable[0];last=focusable[focusable.length-1];first.focus();document.addEventListener('keydown',trap);});// trap tab functionfunction trap(e){if(e.key!=='Tab')return;if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}}close.addEventListener('click',()=>{modal.style.display='none';modal.setAttribute('aria-hidden','true');page.setAttribute('aria-hidden','false');document.removeEventListener('keydown',trap);open.focus();});</script>

Roving tabindex patroon (geselecteerde item krijgt tabindex=0)

<div role="toolbar" aria-label="format"><button tabindex="0">B</button><button tabindex="-1">I</button><button tabindex="-1">U</button></div><script>const toolbar=document.querySelector('[role=\"toolbar\"]');toolbar.addEventListener('keydown',e=>{const items=[...toolbar.querySelectorAll('button')];let i=items.indexOf(document.activeElement);if(e.key==='ArrowRight'){e.preventDefault();items[(i+1)%items.length].focus();}if(e.key==='ArrowLeft'){e.preventDefault();items[(i-1+items.length)%items.length].focus();}});toolbar.addEventListener('focusin',e=>{items=[...toolbar.querySelectorAll('button')];items.forEach(btn=>btn.setAttribute('tabindex',btn===document.activeElement?0:-1));});</script>

Checklist voor developers

  • Gebruik semantische elementen (button, a, input) vóór role/ARIA
  • Voeg zichtbare focus-styles toe met :focus-visible
  • Behandel Enter/Space op custom controls en zorg voor keyboard handlers
  • Geen tabindex>0; gebruik tabindex=”0″ of roving tabindex patroon
  • Modals: aria-modal=”true”, aria-hidden op achtergrond, focus trap, restore focus
  • Formulieren: focus op eerste fout en aria-invalid/aria-describedby koppelen
  • Controleer screenreader-labels: use aria-label, aria-labelledby, and descriptive text

Tips voor designers en redacties

Designers — visuele focus & contrast

Zorg dat focus-indicatoren voldoen aan contrast en grootte (minimaal 2px zichtbaar surround of 3px box-shadow). Gebruik component-states (focus, hover, active) in het design system en documenteer keyboard-gedrag.

Redacties — content en interacties

Schrijf korte, betekenisvolle knopteksten; vermijd “klik hier”. Markeer interactieve elementen consistent. Plaats skip-links en zorg dat error summaries duidelijk worden gemarkeerd en programmatically focusable.

Hoe test je dit?

Handmatige tests — stappenplan

  1. Verlaat muis en navigeer volledig met Tab/Shift+Tab. Zijn alle interactieve items bereikbaar en in logische volgorde?
  2. Gebruik Enter en Space op alle knoppen en interactieve custom elementen. Werken ze identiek? (Voor links: Enter; voor buttons: Enter/Space)
  3. Open modals en controleer focus trap: tab moet binnen modal blijven; Escape moet sluiten (indien ontworpen).
  4. Inspecteer focus-indicatoren visueel en met gezette contrastmeter.
  5. Activeer foutscenario in formulier: focus moet springen naar foutoverzicht en ieder foutveld moet aria-describedby gebruiken.

Automated & tooling

Run onze WCAG checker voor geautomatiseerde tests en aanvullende aanbevelingen. Installeer de wcagtool plugin voor browser-integratie (axe + custom checks). Gebruik browser Accessibility tree (Chrome DevTools) om aria-hidden en tabindex te verifiëren.

Concrete testcases

Maak een testpagina met: skip link, 5 knoppen inclusief custom control, modal, formulier met 2 fouten. Test tabvolgorde, Enter/Space, screenreader-toegankelijkheid (NVDA/VoiceOver) en voer onze checker uit.

Extra concrete how-to’s

Formulierfouten automatisch focussen

function focusFirstError(){const errorEl=document.querySelector('.error, [aria-invalid=\"true\"]');if(!errorEl)return;errorEl.setAttribute('tabindex','-1');errorEl.focus();}

Roep focusFirstError() aan na server/validator response. Verwijder tijdelijke tabindex na focus indien nodig.

Prevent default op Space voor non-button elementen

element.addEventListener('keydown',e=>{if(e.key===' '){e.preventDefault();element.click();}});

Hoe wij je direct helpen

Gebruik de WCAG checker om je pagina nu te scannen. Download onze integratie op wcagtool.nl/plugin voor CI/CD checks. Zit je vast? Stuur je vraag via het contactformulier — antwoord binnen 24 uur.

Direct toepasbare code: gebruik de modal-focus-trap code hierboven als copy-paste basis en test het met onze checker.

Praktische tip: voeg in je CI een test die met puppeteer tab-strings simuleert en checkt of focus nooit buiten een open modal terechtkomt — combineer dat met onze plugin voor automatische rapportage.