Tabellen toegankelijk maken: structuur en betekenis

Praktische implementatie van WCAG – wcagtool.nl

WCAG praktisch toepassen: concreet, testbaar, direct inzetbaar

Toegankelijkheid faalt vaak omdat regels abstract blijven, implementatie versnipperd is en teams niet weten welke code direct voldoet aan WCAG. Wij vertalen WCAG naar werkbare patronen: concrete HTML/ARIA/CSS/JS-snippets, checklist-stappen en testinstructies zodat jouw site echt toegankelijk wordt.

Dit artikel behandelt veelvoorkomende praktijkfouten en biedt directe oplossingen voor forms, navigatie, modals, dynamische content, contrast en toetsenbordbediening. Gebruik onze WCAG checker, download onze plugin via wcagtool.nl/plugin en neem contact op via wcagtool.nl/contact — vragen beantwoorden we binnen 24 uur.

Het probleem in de praktijk

Belangrijkste issues die we tegenkomen: ontbrekende labels en focus, custom controls zonder ARIA/keyboard, modals die keyboard- en screenreader-ondersteuning missen, onvoldoende contrast en onduidelijke foutmeldingen in formulieren. Vaak zijn oplossingen half-af: er is een skip-link maar die is onzichtbaar, of een modal sluit niet met Escape.

Deze fouten ontstaan door gebrek aan concrete implementatiestappen en ongeteste custom components. Wij lossen dit op met herbruikbare codepatronen, standaard checklisten en automatische checks met onze validator zodat je direct kunt verifiëren of wijzigingen voldoen.

Zo los je dit op in code

1) Semantische basis: kopjes, landmarks en skip-links

Waarom: screenreaders en toetsenbordgebruikers navigeren op landmarks en headings. Stap-voor-stap:

  • Zorg voor één h1 per pagina.
  • Gebruik landmarks: <header>, <nav>, <main role=”main”>, <footer>.
  • Voeg een zichtbare skip-link toe die zichtbaar wordt bij focus.
<a class="skip-link" href="#main">Spring naar inhoud</a>
<header>...</header>
<main id="main" role="main">...</main>
.skip-link {position:absolute;left:-999px;top:auto;width:1px;height:1px;overflow:hidden;} .skip-link:focus {left:10px;top:10px;width:auto;height:auto;padding:8px;background:#000;color:#fff;z-index:1000;} 

2) Toegankelijke formulieren: labels, errors en aria

Probleem: labels missen, foutmeldingen niet gestructureerd. Oplossing: expliciete <label> gekoppeld aan <input>, aria-invalid en aria-describedby voor fouttekst.

<label for="email">E-mail</label>
<input id="email" name="email" type="email" aria-describedby="email-help email-error" aria-invalid="false" required>
<div id="email-help">We gebruiken je e-mail alleen voor...
<div id="email-error" role="alert" aria-live="assertive"><!-- lege container voor JS-fouten --></div>

JS-validatie (voorbeeld):

function validateEmail(){const el=document.getElementById('email');const err=document.getElementById('email-error');if(!el.checkValidity()){el.setAttribute('aria-invalid','true');err.textContent=el.validationMessage;err.focus();return false;}el.setAttribute('aria-invalid','false');err.textContent='';return true;}

3) Custom controls en keyboard-ondersteuning

Veel custom dropdowns en toggles breken keyboard. Basisregels: focusable element (tabindex=”0″), role passend, keyboard handlers voor Enter/Space/Arrow keys en ARIA-activedescendant of aria-selected.

<div class="dropdown" role="listbox" tabindex="0" aria-activedescendant="opt-1">
<div id="opt-1" role="option" aria-selected="true">Optie 1</div>
<div id="opt-2" role="option">Optie 2</div>
</div>
document.querySelector('.dropdown').addEventListener('keydown', e => {const box=e.currentTarget; if(e.key==='ArrowDown'){/* move focus to next option */} if(e.key==='Enter' || e.key===' '){/* select */}});

4) Toegankelijke modals: focus trap, aria-hidden en Escape

Veelvoorkomend fout: focus verdwijnt buiten modal of screenreader leest onderliggende content. Basisimplementatie:

<button id="openModal">Open</button>
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-hidden="true">
<h2 id="modalTitle">Titel</h2>
<button id="closeModal">Sluit</button>
</div>
const open=document.getElementById('openModal');const modal=document.getElementById('modal');const close=document.getElementById('closeModal');let lastFocused=null;open.addEventListener('click',()=>{lastFocused=document.activeElement;modal.setAttribute('aria-hidden','false');document.body.style.overflow='hidden';modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])').focus();trapFocus(modal);});close.addEventListener('click',closeModal);function closeModal(){modal.setAttribute('aria-hidden','true');document.body.style.overflow='';lastFocused && lastFocused.focus();releaseTrap();}
// eenvoudige focus trap (productie: gebruik well-tested library)let focusableElements=[];let firstEl=null;let lastEl=null;function trapFocus(container){focusableElements=Array.from(container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'));firstEl=focusableElements[0];lastEl=focusableElements[focusableElements.length-1];container.addEventListener('keydown',handleTrap); }function handleTrap(e){if(e.key==='Tab'){if(e.shiftKey && document.activeElement===firstEl){e.preventDefault();lastEl.focus();}else if(!e.shiftKey && document.activeElement===lastEl){e.preventDefault();firstEl.focus();}}if(e.key==='Escape'){closeModal();}}function releaseTrap(){modal.removeEventListener('keydown',handleTrap);}

5) Contrast en kleurgebruik

Praktisch: gebruik CSS-variabelen en fallback checks. Automatische check: contrast ratio >= 4.5 voor body tekst (AA) en 3.0 voor large text.

:root{--text:#222;--bg:#fff;} .contrast-example{color:var(--text);background:var(--bg);} /* test contrast met onze checker of tools zoals axe, contrast-ratio */

Checklist voor developers

  • Semantische HTML: headings, landmarks en correcte elementtypes
  • Formulieren: expliciete <label>, aria-describedby voor hulp en fouten, role=”alert” voor foutmeldingen
  • Toetsenbord: alle interactieve controls focusable en werkend met Enter/Space/Arrows
  • Focusbeeld: maak :focus en :focus-visible duidelijk zichtbaar (minstens 3px contrast)
  • Modals: aria-modal=”true”, focus trap, escape werkt, onderliggende content aria-hidden
  • Images: alt-teksten, role=”presentation” waar nodig
  • Media: ondertiteling, transcript en audio descriptions waar relevant
  • Contrast: controleer ratio’s en gebruik variabelen / design tokens
  • ARIA: gebruik alleen als semantiek ontbreekt, test met screenreader
  • Automatische tests: axe-core, pa11y, Lighthouse en onze WCAG checker

Tips voor designers en redacties

Design tokens en componentbibliotheek

Maak toegankelijke design tokens: kleurvariabelen, typografie en focus-stijlen centraal beheren. Voorbeeld tokens in CSS/SCSS en implementatie in componenten zorgt dat iedereen consistente, toegankelijke keuzes maakt.

$color-text: #1b1b1b; $color-bg: #ffffff; $focus-color: #ffbf47; /* SCSS tokens */

Tekst en content: helder, kort en logisch

Redacties: gebruik korte zinnen, opsommingen en duidelijke linkteksten. Vermijd “klik hier”. Voeg ALT-teksten die context geven en beschrijf complexe afbeeldingen in nabijgelegen tekst of een longdesc-link.

Designers: test prototypes met keyboard en screenreaders

Laat developers niet pas in de bouwfase accessibility oplossen. Test interactieve prototypes met Tab, Space, Enter en NVDA/VoiceOver early-stage. Gebruik onze plugin om prototypes snel te scannen: download plugin.

Hoe test je dit?

Automated tests — snel en herhaalbaar

  • axe-core: NPM-installatie en quick-run in CI: npm i axe-core@latest –save-dev. Gebruik axe-core in end-to-end tests (Puppeteer/Playwright).
  • pa11y: command-line rapporten voor pagina’s.
  • Lighthouse CI: meet WCAG-gerelateerde audits.
  • Onze WCAG checker: snelle website-scan en rapport met prioriteiten.
// voorbeeld: korte axe run in browserconsole (open page)
import('https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.4.1/axe.min.js').then(()=>axe.run().then(results => console.log(results))).catch(console.error);

Handmatige tests — essentieel

  • Toetsenbord-only: tab, shift+tab, Enter, Space, Arrow keys, Escape. Controleer focusvolgorde en zichtbaarheid.
  • Screenreader: NVDA (Windows), VoiceOver (macOS/iOS) en TalkBack (Android). Luister of content logisch wordt voorgelezen en of ARIA-roles kloppen.
  • Contrast: gebruik onze checker of WebAIM Contrast Checker; test bij verschillende zoomniveaus.
  • Formulieren: voer foutieve waarden in — worden foutmeldingen voorgelezen en geassocieerd?
  • Responsiveness & zoom: zoom tot 200% zonder layout break.

Concrete teststappen (rapid checklist)

  1. Scan pagina met WCAG checker en exporteer rapport.
  2. Run axe/pa11y in CI en fix high priority issues.
  3. Manueel: tab door belangrijkste flows (login, checkout, formulieren).
  4. Test modals: openen, tab, Shift+Tab, Escape sluit en focus terug.
  5. Screenreader-run: lees pagina top-down, verifieer headings en links.
  6. Contrast-check op belangrijkste kleuren en call-to-actions.
  7. Laat content-editors ALT-teksten en linkteksten controleren via checklist hieronder.

Specifieke mini-how-to’s

Hoe linkteksten redacteur-proof maken

Regel: linktekst geeft context out-of-context. Voorbeeld van anti-pattern “klik hier” en verbeterde variant:

<!-- Slecht -->
<a href="/rapport">Klik hier</a>
<!-- Goed -->
<a href="/rapport">Download het toegankelijkheidsrapport (PDF)</a>

Hoe maak je een toegankelijke tabel

Gebruik <table> semantiek met captions en headers:

<table>
<caption>Jaarresultaten per product</caption>
<thead><tr><th scope="col">Product</th><th scope="col">2024</th></tr></thead>
<tbody><tr><th scope="row">A</th><td>€1.000</td></tr></tbody>
</table>

Snelle proxy voor kleurcontrast in CSS

/* voeg een hoge-contrast toggle voor testing */ .high-contrast { --text:#000; --bg:#fff; } body.high-contrast * { color:var(--text) !important; background:var(--bg) !important; }

Extra checklists voor content-editors

  • Alle afbeeldingen hebben beschrijvende alt-teksten of role=”presentation”.
  • Video’s hebben ondertiteling en transcript.
  • Links zijn beschrijvend en uniek in context.
  • Formuliervelden hebben labels en hulpteksten.
  • Titels en meta descriptions zijn zinvol (SEO & accessibility).

Tools en commando’s

  • Local axe run (puppeteer/playwright): zie axe docs of gebruik onze CI-integratie via WCAG checker.
  • pa11y: npm i -g pa11y && pa11y https://jouwsite.nl
  • Lighthouse: open DevTools > Lighthouse of run lighthouse-ci.
  • WCAG plugin: download hier voor snelle scans in de browser.

Wil je dat wij meekijken? Upload je URL naar onze WCAG checker, installeer de plugin en stuur een screenshot via ons contactformulier. Antwoord binnen 24 uur.

Praktische tip: voeg in je CI-pipeline een stap toe die onze checker of axe uitvoert; blokkeer deploys bij critical/accessibility failures. Voorbeeld GitHub Action (kort):

name: a11y-checkon: pushjobs: a11y: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run axe-ci run: | npm ci && npx axe --url=https://staging.jouwsite.nl --format=json --output=reports/a11y.json

Gebruik onze checker nu om je pagina te scannen, download de plugin voor snelle lokale tests en vraag hulp via ons contactformulier — we reageren binnen 24 uur.

Laatste praktische check: plak deze snippet in de console op een pagina en controleer of er focusable elements zijn zonder visible focus (als resultaat een kort overzicht):

Array.from(document.querySelectorAll('a,button,input,textarea,select,[tabindex]')).filter(el => {const s=getComputedStyle(el);const hasVisibleFocus = s.outlineStyle!=='none' || s.boxShadow!=='none';return el.tabIndex >= 0 && s.visibility!=='hidden' && s.display!=='none' && !hasVisibleFocus;}).slice(0,20)