Toegankelijke toetsenbordnavigatie en focusbeheer
Toetsenbordnavigatie en focusbeheer zijn in de praktijk vaak fout uitgevoerd: skip-links ontbreken of worden onzichtbaar gemaakt, modals slopen focus, SPA-route-wissels verplaatsen geen focus en custom components missen ARIA en tabindex-logica. Resultaat: keyboard-only gebruikers en schermlezers lopen vast.
Wij bieden concrete, testbare oplossingen: stap-voor-stap codevoorbeelden voor HTML/ARIA/CSS/JS, checklisten voor developers en praktische instructies voor designers en redacties. Test direct met onze WCAG checker, download onze plugin via wcagtool.nl/plugin en vraag hulp via het contactformulier (vragen worden binnen 24 uur beantwoord).
Het probleem in de praktijk
Veelvoorkomende fouten
- Geen skip-link of skip-link die pas zichtbaar wordt ná focus, of die geen focus-ontoegankelijk element target.
- Custom controls zonder tabindex of ARIA waardoor niet bereikbaar met toetsenbord.
- Modals en dialogs zetten focus niet correct, of laten focus ontsnappen (geen focus-trap).
- SPA navigatie verandert URL maar verplaatst geen focus naar begin van de content.
- Onzichtbare maar focusbare elementen of visuele outlines worden verwijderd zonder alternatief.
Waarom dat kritisch is
WCAG-eisen zoals 2.1.1 (Keyboard), 2.4.3 (Focus Order), 2.4.7 (Visible Focus) en 4.1.2 (Name, Role, Value) worden vaak verbroken. Dit betekent slechtere gebruikservaring en juridische risico’s. Praktische implementatie voorkomt dat.
Zo los je dit op in code
1) Skip-link: standaard en zichtbaar bij focus
Implementatie: voeg een skip-link bovenaan je page, maak hem visueel verborgen maar zichtbaar bij :focus. Test: Tab als eerste element en controleer zichtbaarheid.
<a class="skip-link" href="#main">Sla navigatie over</a><br><!-- CSS --><br>.skip-link{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}<br>.skip-link:focus{position:static;left:auto;top:auto;width:auto;height:auto;padding:0.5rem;background:#005a9c;color:#fff;z-index:1000}
2) Focus-visible en outline: behoud zichtbare focus
Gebruik :focus-visible om geen false negatives te creëren en behoud outline voor keyboardgebruikers.
/* CSS */<br>:focus{outline:none}<br>:focus-visible{outline:3px solid #ffb84d;outline-offset:2px;border-radius:2px}
3) Accessible custom button / role=”button”
Als je een element opbouwt als knop, voeg de juiste ARIA, tabindex en keyboard handlers toe. Gebruik native <button> waar mogelijk.
<!-- HTML --><br><div role="button" tabindex="0" aria-pressed="false" id="likeBtn">Like</div><br><!-- JS --><br>document.getElementById('likeBtn').addEventListener('keydown', function(e){ if(e.key==='Enter' || e.key===' '){ e.preventDefault(); this.click(); } });
4) Modal / dialog: focus-trap en aria-modal
Stappen: 1) geef dialog role=”dialog” en aria-modal=”true”; 2) zet focus op eerste focusable element of op container met tabindex=”-1″; 3) implementeer focus-trap (tab, shift+tab) en sluit met Escape; 4) restore focus naar opener bij sluiten.
<!-- HTML --><br><div id="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" tabindex="-1" hidden><br><h2 id="modalTitle">Instellingen</h2><br><button id="closeModal">Sluiten</button><br></div><br><!-- JS (basis focus-trap) --><br>const modal=document.getElementById('modal'); const opener=document.getElementById('openModal'); opener.addEventListener('click', ()=>{ modal.hidden=false; modal.focus(); }); modal.addEventListener('keydown', e=>{ if(e.key==='Escape') closeModal(); if(e.key==='Tab'){ const focusables = modal.querySelectorAll('a[href],button,textarea,input,select,[tabindex]:not([tabindex=\"-1\"])'); const first = focusables[0]; const last = focusables[focusables.length-1]; if(e.shiftKey && document.activeElement===first){ e.preventDefault(); last.focus(); } else if(!e.shiftKey && document.activeElement===last){ e.preventDefault(); first.focus(); } } }); function closeModal(){ modal.hidden=true; opener.focus(); }
5) Single Page Applications: focus bij route-wissel
Stappen: na route change, verplaats focus naar een element met role=”main” of header van de content en geef het tabindex=”-1″ indien nodig.
// pseudo-code voor router hooks<br>router.on('routeChangeComplete', ()=>{ const main=document.querySelector('main'); if(main){ main.setAttribute('tabindex','-1'); main.focus(); // optioneel remove attribute na eerste focus } });
6) Verberg visueel maar niet voor AT: use aria-hidden correct
Als je content visueel verborgen maakt voor layout, zorg dat screen readers niet per ongeluk uitgesloten of dubbel voorgelicht worden. Gebruik aria-hidden en inert/hidden zorgvuldig.
<!-- correct: visueel verbergen voor AT --><br><div aria-hidden="false">Zichtbaar voor screenreaders</div><br><!-- verbergen voor AT --><br><div aria-hidden="true">Verborgen voor screenreaders</div>
Checklist voor developers
- Is een skip-link aanwezig en focusable?
- Heeft elk interactief element een semantische tag of role + tabindex?
- Behoud je focus-outline of gebruik je :focus-visible correct?
- Gebruiken modals role=”dialog”, aria-modal en een focus-trap?
- Restore focus naar opener na sluiten dialog/modal?
- Wisselt focus correct na SPA-route changes?
- Test met keyboard-only, schermlezer en onze WCAG checker?
Tips voor designers en redacties
Design tokens en focus-states
Wireframe en definieer focus-states in het component library. Gebruik contrast- en zichtbaarheidstoetsen: focus-outline moet voldoen aan kleurcontrastregels.
Content-editing: geen interactieve content in headings
Moedig redacteurs aan om geen interactieve elementen te plaatsen in koppen zonder voldoende ARIA en tabindex; geef templates met correct gebruikte <button> en links.
Component library regels
- Altijd native elementen gebruiken waar mogelijk (<button>, <a>).
- Documenteer ARIA, keyboard flows en expected focus order per component.
- Automatiseer tests in CI met onze plugin voor component libraries.
Hoe test je dit?
Handmatige tests (snel en effectief)
- Keyboard-only navigatie: tab door pagina, zorg dat alle interactieve items bereikbaar en logisch zijn.
- Focus order: gebruik Shift+Tab en Tab om te controleren of volgorde logisch is.
- Modals: open modal, probeer alles met Tab en Escape, en sluit modal; focus moet terug naar opener.
- Skip-link: Tab als eerste element en activeer skip-link; content moet meteen focus krijgen.
Automated checks en our tools
Gebruik onze WCAG checker voor geautomatiseerde scans (inclusief keyboard checks en missing ARIA). Installeer de wcagtool plugin in je CI/CD pipeline voor regressietests. Vragen? Gebruik het contactformulier — we reageren binnen 24 uur.
Screenreader tests
Test met NVDA/JAWS (Windows) en VoiceOver (macOS/iOS). Controleer dat labels, rollen en statusinformatie (aria-live) correct en niet dubbel wordt voorgelezen.
Browser devtools
Gebruik Accessibility pane (Chrome/Edge) om accessibility tree te inspecteren: check role, name, focusable, aria-hidden en tabindex.
Praktische mini-how-to’s
Quick-fix: maak alle custom controls keyboard-accessible
// Voor elk custom control: voeg attribuut en keydown handler toe<br>el.setAttribute('role','button'); el.setAttribute('tabindex','0'); el.addEventListener('keydown', e => { if(e.key==='Enter' || e.key===' ') { e.preventDefault(); el.click(); } });
Mini-script: restore focus na AJAX content load
// Plaats dit in je fetch callback<br>fetch('/page/section').then(r=>r.text()).then(html=>{ main.innerHTML=html; main.setAttribute('tabindex','-1'); main.focus(); // verwijder tabindex daarna main.removeAttribute('tabindex'); });
Verifiëren met onze checker via CLI (voorbeeld)
// gebruik onze plugin/CLI indien beschikbaar (voorbeeld) <br>npm run wcagtool -- --url=https://jouwsite.nl
Extra checklists en foutopsporing
Als focus- outline toch weg is
- Controleer CSS: zoek naar outline:none global rules.
- Gebruik :focus-visible waar mogelijk.
- Voeg fallback style toe voor oudere browsers.
Als modal niet focusable is
- Controleer of container tabindex=”-1″ heeft.
- Zet focus expliciet met element.focus() na tonen.
- Controleer of aria-hidden op achtergrond niet interfereert.
Hoe wij je kunnen helpen
Laat ons je site scannen met de WCAG checker, installeer onze plugin voor CI-tests en vraag advies via het contactformulier. Reactietijd: binnen 24 uur. We leveren concrete code-patches, pull-request ready fixes en component-library updates.
Direct toepasbare tip: voeg dit korte CSS-fragment toe aan je globale stylesheet om meteen veel focus-problemen op te lossen:
/* Plak in globals.css */<br>:focus{outline:none}<br>:focus-visible{outline:3px solid #0078d4;outline-offset:2px}
Test nu direct je website met onze WCAG checker, download de plugin voor geautomatiseerde tests en neem contact op via het contactformulier voor specifieke implementatievragen (antwoord binnen 24 uur).