Info en relaties en semantische HTML

Als je een website gebruikt zonder dat je het scherm kunt zien. Hoe weet je dan:

  • Welke tekst een kop is en welk niveau deze heeft?
  • Welke items bij elkaar horen in een lijst?
  • Welke cellen bij welke rijen of kolommen horen in een tabel?

Als je alleen visuele opmaak gebruikt (zoals vetgedrukte tekst of grotere lettertypen) zonder semantische HTML, dan mist een gebruiker van een schermlezer deze structuur.

Wat is WCAG 1.3.1 eigenlijk?

Succescriterium 1.3.1 Info en relaties is een van de belangrijke WCAG-succescriteria. Het criterium stelt:

Informatie, structuur en relaties overgebracht door presentatie kunnen door software bepaald worden of zijn beschikbaar in tekst.

Simpel gezegd: Alle informatie, structuur en relaties die iemand visueel kan waarnemen, moeten ook in de code worden vastgelegd. Hierdoor zijn deze ook beschikbaar voor mensen die hulptechnologieën gebruiken, zoals een schermlezer. En dat is precies waar semantische HTML in beeld komt.

Semantische HTML is de belangrijkste manier om aan dit criterium te voldoen, maar het gaat iets breder. Ook informatie die door andere visuele middelen wordt overgebracht moet programmatisch bepaalbaar zijn.

Waarom semantische HTML zo belangrijk is

Semantische HTML gebruikt elementen in de code die betekenis geven aan je content, in plaats van alleen te focussen op hoe het eruit ziet.

Vergelijk deze twee voorbeelden:

<!-- Niet-semantisch -->
<div class="title">Mijn hoofdkop</div>
<div class="subtitle">Mijn subkop</div>

<!-- Semantisch -->
<h1>Mijn hoofdkop</h1>
<h2>Mijn subkop</h2>

Voor iemand die goed kan zien is er misschien geen verschil als je de juiste CSS toepast, maar voor een schermlezer is er wél verschil. In het semantische voorbeeld weet hulptechnologie dat het met koppen te maken heeft en wat hun onderlinge hiërarchie is.

Koppen

Laten we beginnen met koppen. Deze zijn ontzettend belangrijk voor de structuur van je pagina.

Waarom goede koppen belangrijk zijn

Koppen zorgen voor:

  1. Context voor de inhoud die volgt
  2. Navigatiepunten voor gebruikers van een schermlezer

Wist je dat mensen die een schermlezer gebruiken vaak navigeren door de koppen op een pagina? Door op één toets te drukken kunnen ze van kop naar kop springen. Als je kopstructuur niet klopt, dan is dat alsof je de inhoudsopgave van een boek helemaal door elkaar haalt.

In de Rotor van VoiceOver ziet dit er zo uit:

Schermafbeelding van een VoiceOver Rotor met een overzicht van koppen van niveau 1, 2, 3, en 4.

Best practices voor koppen

Vermijd zo veel mogelijk het overslaan van kopniveaus. Ga van h1 naar h2 naar h3 en spring dus niet van h1 naar h3.

<!-- Slecht voorbeeld -->
<h1>Productoverzicht</h1>
<h3>Elektronica</h3> <!-- Sprong van h1 naar h3 -->
<h6>Smartphones</h6> <!-- Sprong van h3 naar h6 -->

<!-- Goed voorbeeld -->
<h1>Productoverzicht</h1>
  <h2>Elektronica</h2>
    <h3>Smartphones</h3>
    <h3>Laptops</h3>
  <h2>Kleding</h2>
    <h3>Dames</h3>
    <h3>Heren</h3>

Begin de content van elke pagina met een h1 en zorg dat deze kop de inhoud van de pagina beschrijft.

Veelgemaakte fouten

  • Tekst die een kop is, wordt in de code niet opgemaakt met een kop-element
  • Kop-elementen worden alleen maar gebruikt omdat ze er “mooi” uitzien (voor het ontwerp of de stijl)
  • Kopniveaus worden overgeslagen, waardoor de betekenis van de content verloren gaat
<!-- Slecht voorbeeld -->
<div class="heading">Belangrijke sectie</div>

<!-- Goed voorbeeld -->
<h2>Belangrijke sectie</h2>

Lijsten

Lijsten maken je tekst makkelijker te lezen en te begrijpen.

Soorten lijsten

Er zijn drie soorten lijsten om gerelateerde items te groeperen en hiërarchie aan te brengen:

  • Ongeordende lijst of opsomming
  • Geordende of genummerde lijst
  • Definitielijst
<!-- Ongeordende lijst -->
<ul>
  <li>Appels</li>
  <li>Bananen</li>
  <li>Peren</li>
</ul>

<!-- Geordende lijst -->
<ol>
  <li>Meng de ingrediënten</li>
  <li>Bak het deeg</li>
  <li>Laat het afkoelen</li>
</ol>

<!-- Definitielijst -->
<dl>
  <dt>HTML</dt>
  <dd>HyperText Markup Language, de standaardtaal voor webpagina's</dd>
  <dt>CSS</dt>
  <dd>Cascading Style Sheets, voor de opmaak van webpagina's</dd>
</dl>

Schermlezers kondigen het type lijst aan en ook het aantal items in de lijst. Hierdoor weet de gebruiker wat te verwachten.

Veelgemaakte fouten

  • Tekst wordt geplaatst met handmatige opsommingstekens of nummering
  • Geneste lijsten worden niet juist gestructureerd
<!-- Slecht voorbeeld -->
<div>- Eerste item</div>
<div>- Tweede item</div>
<div>- Derde item</div>

<!-- Goed voorbeeld -->
<ul>
  <li>Eerste item</li>
  <li>Tweede item</li>
  <li>Derde item</li>
</ul>

Een tip: als je visuals gebruikt voor je bullets via CSS, zorg dan dat je nog steeds de semantische lijst-elementen behoudt. Je kunt list-style: none gebruiken en dan je eigen bullets toevoegen met CSS.

Tabellen

Tabellen gebruik je om gegevens te presenteren of te vergelijken. Tabellen worden vaak verkeerd gebruikt. Ze zijn echt bedoeld voor tabulaire gegevens, en dus niet voor layout.

Een toegankelijke tabel maken

Een toegankelijke tabel heeft altijd:

  • <th>: Tabelkoppen (met scope-attribuut)
  • <td>: Tabeldata (cellen)
  • <caption>: Beschrijft waar de tabel over gaat
  • <thead><tbody><tfoot>: Structureren de tabel in logische secties
<table>
  <caption>Aantal succescriteria per WCAG niveau</caption>
  <thead>
    <tr>
      <th scope="col">WCAG versie</th>
      <th scope="col">Niveau A</th>
      <th scope="col">Niveau AA</th>
      <th scope="col">Niveau AAA</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">WCAG 2.1</th>
      <td>30</td>
      <td>50</td>
      <td>78</td>
    </tr>
    <tr>
      <th scope="row">WCAG 2.2</th>
      <td>30</td>
      <td>54</td>
      <td>86</td>
    </tr>
  </tbody>
</table>

Het scope-attribuut is belangrijk. Het vertelt hulptechnologie of een tabelkop bij een rij of kolom hoort. Gebruik dit attribuut altijd als er koppen in twee richtingen zijn. Gebruik de waarde col voor kolom en row voor rij.

Bij eenvoudige tabellen met koppen alleen in de eerste rij bepalen moderne schermlezers vaak zelf dat de koppen bij de kolommen eronder horen.

Complexe tabellen

Voor complexere tabellen kun je headers en id gebruiken:

<table>
  <caption>Kwartaalcijfers per afdeling</caption>
  <tr>
    <td></td>
    <th id="q1">Kwartaal 1</th>
    <th id="q2">Kwartaal 2</th>
    <th id="q3">Kwartaal 3</th>
    <th id="q4">Kwartaal 4</th>
  </tr>
  <tr>
    <th id="sales">Verkoop</th>
    <td headers="sales q1">€10K</td>
    <td headers="sales q2">€12K</td>
    <td headers="sales q3">€15K</td>
    <td headers="sales q4">€18K</td>
  </tr>
  <tr>
    <th id="marketing">Marketing</th>
    <td headers="marketing q1">€5K</td>
    <td headers="marketing q2">€6K</td>
    <td headers="marketing q3">€7K</td>
    <td headers="marketing q4">€8K</td>
  </tr>
</table>

Door de headers-attributen kan een schermlezer hier aankondigen: “Verkoop Kwartaal 1 €10K”. Zo weet de gebruiker precies welke gegevens bij welke koppen horen.

Veelgemaakte fouten

  • Tabellen worden gebruikt voor de opmaak van een pagina
  • Datatabellen zonder tabelkoppen
<!-- Slecht voorbeeld -->
<table>
  <tbody>
    <tr>
      <th scope="row">Januari</th>
      <td>€750</td>
      <td>€300</td>
      <td>€1050</td>
    </tr>
    <tr>
      <th scope="row">Februari</th>
      <td>€750</td>
      <td>€280</td>
      <td>€1030</td>
    </tr>
  </tbody>
</table>

<!-- Goed voorbeeld -->
<table>
  <caption>Maandelijkse uitgaven</caption>
  <thead>
    <tr>
      <th scope="col">Maand</th>
      <th scope="col">Huur</th>
      <th scope="col">Eten</th>
      <th scope="col">Totaal</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Januari</th>
      <td>€750</td>
      <td>€300</td>
      <td>€1050</td>
    </tr>
    <tr>
      <th scope="row">Februari</th>
      <td>€750</td>
      <td>€280</td>
      <td>€1030</td>
    </tr>
  </tbody>
</table>

Formulieren

Labels

Elk invoerveld moet een programmatisch gekoppeld label hebben. Dit doe je met het for-attribuut dat verwijst naar het id van het invoerveld, of door het invoerveld binnen het <label>-element te plaatsen.

<!-- for en id -->
<label for="email">E-mailadres</label>
<input type="email" id="email" name="email">

<!-- Nesting -->
<label>
  E-mailadres
  <input type="email" name="email">
</label>

Een schermlezer leest bij focus op het invoerveld automatisch het gekoppelde label voor. Zonder die koppeling weet de gebruiker niet welke informatie wordt verwacht.

Groepen van invoervelden

Groepeer gerelateerde invoervelden met <fieldset> en <legend>. Dit is vooral belangrijk bij keuzerondjes en selectievakjes die samen één vraag beantwoorden.

<fieldset>
  <legend>
    Bezorgvoorkeuren
  </legend>
  <label>
    <input type="radio" name="bezorging" value="thuis">
    Thuisbezorgen
  </label>
  <label>
    <input type="radio" name="bezorging" value="ophalen">
    Ophalen in winkel
  </label>
</fieldset>

De schermlezer kondigt eerst de <legend> aan (“Bezorgvoorkeuren”) en vervolgens elk individueel label. Zo begrijpt de gebruiker de context.

Veelvoorkomende fouten

  • Labels die visueel naast het veld staan maar niet programmatisch gekoppeld zijn.
  • Groepen selectievakjes zonder <fieldset>.
<!-- Slecht voorbeeld -->
<span>
  E-mailadres
</span>
<input type="email" name="email">

<!-- Goed voorbeeld -->
<label for="email">
  E-mailadres
</label>
<input type="email" id="email" name="email">

<!-- Slecht voorbeeld -->
<p>
  Hoe wil je betalen?
</p>
<input type="radio" name="betaling" value="ideal">
iDEAL
<input type="radio" name="betaling" value="creditcard">
Creditcard

<!-- Goed voorbeeld -->
<fieldset>
  <legend>
    Hoe wil je betalen?
  </legend>
  <label>
    <input type="radio" name="betaling" value="ideal">
    iDEAL
  </label>
  <label>
    <input type="radio" name="betaling" value="creditcard">
    Creditcard
  </label>
</fieldset>

Praktische tips voor het testen

Om je implementatie te controleren:

  1. Schakel je CSS uit: Is je content nog steeds logisch en begrijpelijk?
  2. Controleer je kopstructuur: Beschrijft het op hoofdlijnen de inhoud van de pagina?
  3. Gebruik zelf eens een schermlezer: NVDA (Windows) of VoiceOver (Mac) zijn goede opties
  4. Gebruik de Web Developer Toolbar: Om HTML-elementen te markeren

Lees meer over hulpmiddelen

Conclusie

Semantische HTML is geen extra werk maar eigenlijk de enige juiste manier van ontwikkelen.

Heb je vragen over specifieke implementaties? Laat het me weten!