Chapitre 3.1 — Pourquoi la sémantique
⏱️ TL;DR — Un élément HTML sémantique (
<button>,<nav>,<h1>,<label>…) porte un sens que le navigateur traduit en rôle, focus clavier, activation et états exposés aux technologies d’assistance. Une<div>ou un<span>, eux, ne portent aucun sens : ce sont des boîtes neutres. Remplacer un natif par une<div>stylée, c’est jeter tout ce que la plateforme te donnait gratuitement — et devoir le reconstruire à la main, mal la plupart du temps. Règle de vie : choisis d’abord la bonne balise, ajoute de l’ARIA seulement quand aucun natif ne convient.
🎯 Objectifs
- Distinguer un élément sémantique d’un élément neutre (
<div>/<span>). - Lister ce qu’un natif apporte gratuitement : rôle, focus, clavier, états.
- Comprendre le coût caché de la « soupe de
<div>». - Adopter le réflexe « la plateforme est ton amie » avant de coder un composant.
Sémantique vs soupe de <div>
Le HTML n’est pas qu’un sac de conteneurs à styler. Chaque balise déclare une intention : « ceci est un bouton », « ceci est un titre de niveau 1 », « ceci est la navigation principale ». Le navigateur lit cette intention et la publie dans l’arbre d’accessibilité (voir Partie 5), que consomment lecteurs d’écran, commandes vocales et afficheurs braille.
À l’opposé, la « soupe de <div> » (div soup) est une page où presque tout est une <div> ou un <span>. Visuellement, avec assez de CSS, ça peut ressembler à n’importe quoi. Sémantiquement, c’est le néant : ni titre, ni région, ni bouton, ni lien. Pour un utilisateur de lecteur d’écran, une telle page est une bouillie indifférenciée où plus rien ne s’annonce ni ne se navigue.
<!-- ❌ Soupe de div : rien n'est annoncé, rien n'est navigable -->
<div class="header">
<div class="logo">A11yLearn</div>
<div class="menu">
<div class="menu-item" onclick="go('/cours')">Cours</div>
</div>
</div>
<div class="title-xl">Tableau de bord</div>
<!-- ✅ HTML sémantique : régions, titre et lien exposés gratuitement -->
<header>
<a href="/">A11yLearn</a>
<nav aria-label="Principale">
<a href="/cours">Cours</a>
</nav>
</header>
<h1>Tableau de bord</h1>Ce qu’un élément natif t’apporte — gratuitement
Prends le seul <button>. En écrivant ces sept caractères, tu obtiens sans une ligne de JavaScript :
- Un rôle exposé : le lecteur d’écran annonce « bouton ». L’utilisateur sait que c’est actionnable.
- Le focus clavier : l’élément entre dans l’ordre de tabulation, atteignable avec Tab.
- L’activation clavier : Entrée et Espace déclenchent le clic, exactement comme la souris.
- La gestion des états :
disabledgrise ET retire le focus ET bloque l’activation, d’un coup. - La sémantique de formulaire :
type="submit"soumet le formulaire,type="button"non.
Refaire tout ça sur une <div> demande : role="button", tabindex="0", un gestionnaire keydown pour Entrée et Espace, la gestion de aria-disabled, la prévention du scroll sur Espace… des dizaines de lignes fragiles pour ré-obtenir ce que la balise offrait d’emblée. C’est le sens de la formule : « la plateforme est ton amie » (use the platform). Elle a déjà résolu ces problèmes ; ne les recode pas.
💡 Réflexe — Avant d’écrire une
<div>interactive, pose-toi une seule question : « existe-t-il un élément HTML natif pour ça ? ». Un clic qui agit →<button>. Une navigation →<a href>. Un champ →<input>/<select>/<textarea>. Un repli/dépli →<details>/<summary>. Neuf fois sur dix, le natif existe et te fait gagner du temps et de l’accessibilité.
L’exemple frappant : <div onclick> vs <button>
C’est le cas d’école, celui qui résume toute cette partie. Deux « boutons » identiques à l’écran, deux expériences radicalement opposées.
<!-- ❌ Muet et inaccessible -->
<div class="btn" onclick="sInscrire()">S'inscrire</div>
<!-- ✅ Rôle, focus et clavier gratuits -->
<button type="button" onclick="sInscrire()">S'inscrire</button>Ce que le lecteur d’écran annonce dans chaque cas :
<div onclick> | <button> | |
|---|---|---|
| Annonce | « S’inscrire » (texte brut) | « S’inscrire, bouton » |
| Atteignable au clavier ? | Non (hors de l’ordre de tabulation) | Oui (Tab) |
| Activable au clavier ? | Non (ni Entrée ni Espace) | Oui (Entrée / Espace) |
État disabled géré ? | Non | Oui, nativement |
Le premier existe pour la souris uniquement. Un utilisateur au clavier ou au lecteur d’écran ne peut littéralement pas s’inscrire. Le second marche pour tout le monde — et tu as tapé moins de code.
♿ Côté utilisateur — Une personne aveugle qui tabule dans ta page ne « voit » que les éléments focusables et leurs rôles. Le
<div onclick>est pour elle invisible : le focus saute par-dessus, comme s’il n’existait pas. Elle ne saura même pas qu’un bouton « S’inscrire » est là. Le<button>, lui, s’annonce « S’inscrire, bouton » et s’active — le même pixel à l’écran, mais un mur d’un côté et une porte de l’autre.
Le coût caché du tout-<div>
Choisir la <div> par défaut n’est pas « neutre, on verra plus tard ». C’est contracter une dette immédiate :
- Dette d’accessibilité : rôles, clavier et états à recoder un par un, avec de l’ARIA (Partie 6) — plus de code, plus de bugs, plus de tests.
- Dette de robustesse : ton composant maison suit ta logique, pas la convention du navigateur ; il casse sur des cas que le natif gère déjà (focus,
disabled, formulaire). - Dette de maintenance : chaque nouveau dév doit comprendre ton faux-bouton au lieu de lire un
<button>évident.
Le HTML natif, à l’inverse, est testé par des milliards d’utilisateurs, cohérent entre navigateurs et technologies d’assistance, et gratuit. La sémantique n’est pas une contrainte esthétique : c’est le choix qui coûte le moins tout en accessibilisant par défaut.
🧭 Sur A11yLearn — Le premier audit d’A11yLearn révèle une barre de navigation entièrement en
<div class="nav-item" onclick>et des cartes de cours « cliquables » en<div>. Rien n’est atteignable au clavier, rien ne s’annonce. Notre première correction n’ajoute aucune ligne d’ARIA : on remplace les<div>par des<a href>et des<button>. D’un coup, la navigation clavier fonctionne et le lecteur d’écran annonce liens et boutons. La leçon fondatrice du cours : commencer par la bonne balise, pas par de l’ARIA.
📚 Aller plus loin — Ce chapitre touche deux points centraux du référentiel : le critère 4.1.2 (Nom, rôle et valeur) — chaque composant d’interface doit exposer son nom, son rôle et son état aux technologies d’assistance — et la première règle d’ARIA : « si un élément HTML natif fournit déjà la sémantique et le comportement voulus, utilise-le plutôt que de le réinventer en ARIA ». On approfondit l’arbre d’accessibilité en Partie 5 et ARIA en Partie 6.
✏️ Exercices
Exercice 1 — Nomme ce qui est perdu. On te donne <span class="button" onclick="valider()">Valider</span>. Cite trois choses qu’un <button> natif apporterait et que ce <span> n’a pas.
✅ Solution
Au moins : (1) le rôle « bouton » annoncé par le lecteur d’écran ; (2) le focus clavier (le <span> n’est pas dans l’ordre de tabulation, donc inatteignable au clavier) ; (3) l’activation clavier par Entrée/Espace. On peut ajouter la gestion native de disabled et l’intégration au formulaire (type="submit"). Tout cela devrait sinon être recodé à la main avec role, tabindex et un gestionnaire keydown.
Exercice 2 — Traduis en sémantique. Réécris ce fragment en HTML sémantique sans changer le rendu attendu.
<div class="card" onclick="ouvrir('/cours/1')">
<div class="card-title">React accessible</div>
<div class="card-cta" onclick="sInscrire(1)">S'inscrire</div>
</div>✅ Solution
La carte mène vers une page → c’est un lien. « S’inscrire » déclenche une action sur place → c’est un bouton. On évite d’imbriquer un bouton dans un lien (interdit et ambigu) : on met le lien sur le titre et le bouton à côté.
<article class="card">
<h3><a href="/cours/1">React accessible</a></h3>
<button type="button" onclick="sInscrire(1)">S'inscrire</button>
</article>Le titre devient un vrai titre navigable, le lien un vrai lien, le bouton un vrai bouton — clavier et annonces gratuits.
🧠 Quiz de révision
1. Que veut dire « la soupe de div » et pourquoi est-ce un problème d’accessibilité ?
Une page faite presque exclusivement de <div>/<span>. Ces éléments sont neutres : ils n’exposent ni rôle, ni titre, ni région, ni comportement clavier. Pour un utilisateur de lecteur d’écran, plus rien ne s’annonce ni ne se navigue — l’information sémantique a disparu.
2. Cite trois choses qu’un <button> natif apporte gratuitement.
<button> natif apporte gratuitement.Parmi : le rôle « bouton » annoncé, le focus clavier (ordre de tabulation), l’activation par Entrée/Espace, la gestion native de l’état disabled, et la sémantique de formulaire (type="submit"/"button"). Tout cela sans JavaScript.
3. Que signifie « la plateforme est ton amie » ?
Que le navigateur a déjà résolu les problèmes de rôle, focus, clavier et états dans ses éléments natifs. Plutôt que de recoder ce comportement sur des <div>, on utilise l’élément HTML prévu pour ça — c’est moins de code, plus robuste et accessible par défaut.
4. Pourquoi un <div onclick> est-il inaccessible au clavier ?
<div onclick> est-il inaccessible au clavier ?Parce qu’une <div> n’est pas dans l’ordre de tabulation par défaut (pas focusable) et n’a aucun gestionnaire clavier : ni Entrée ni Espace ne l’activent. Le focus saute par-dessus, l’élément est invisible pour qui n’utilise pas la souris.
5. Quel est le bon ordre : HTML natif ou ARIA d’abord ?
HTML natif d’abord. La première règle d’ARIA est de préférer un élément natif quand il existe. ARIA ne fait rien (ni focus, ni clavier) : il ne fait que décrire. On ne l’ajoute que lorsqu’aucun élément natif ne couvre le besoin.
Chapitre suivant : Structure & landmarks — comment découper une page en régions navigables au lecteur d’écran.