Web frontend · L2 · Section 3/6
Accessibilité (A11y)
Progression
#Accessibilité (A11y)
L'accessibilité n'est pas une option morale : c'est un critère de qualité mesurable. Une interface utilisable au clavier, annoncée correctement aux lecteurs d'écran, contrastée et respectueuse des préférences système est aussi plus robuste pour tout le monde : navigation clavier, formulaires tolérants, états visibles. Ce chapitre vous donne les vérifications concrètes dans l'ordre où elles rapportent le plus.
#Les trois réflexes qui corrigent 80 % des problèmes
- Tout parcourir au clavier. Posez la souris, naviguez à la touche Tab. Chaque interaction doit être atteignable, l'ordre du focus doit suivre l'ordre visuel, et le focus doit être visible. Si vous ne voyez pas où vous êtes, l'utilisateur non plus.
- Coupler libellés et champs. Chaque
inputa sonlabel for, chaque bouton icône a un libellé (aria-labelou texte visuellement masqué), chaque image a unaltadapté à son rôle. - Annoncer les changements. Erreurs de formulaire, fin de chargement, mise à jour d'un compteur : tout ce qui change sans recharger la page doit vivre dans une région
aria-liveou être signalé par le déplacement du focus.
#Modal accessible : le cycle complet
Une boite de dialogue concentre tous les pièges. Voici le cycle à respecter, étape par étape :
Le bouton « Ouvrir » déclenche l'affichage du modal. On mémorise document.activeElement pour pouvoir y revenir : c'est l'élément déclencheur.
L'attribut inert (désormais bien supporté) rend une région non focusable, non cliquable et invisible aux technologies d'assistance : c'est le moyen le plus sûr de masquer l'arrière-plan d'un modal.
#Structure minimale
1<button id="open-settings">Paramètres d'affichage</button>2 3<div id="settings" role="dialog" aria-modal="true" aria-labelledby="settings-title" hidden>4 <h2 id="settings-title">Paramètres d'affichage</h2>5 <button aria-label="Fermer la fenêtre">×</button>6 <form>7 <label for="density">Densité</label>8 <select id="density">9 <option value="compact">Compacte</option>10 <option value="cosy">Confortable</option>11 </select>12 <button>Valider</button>13 </form>14</div>Le JavaScript du cycle (commenté)
let opener = null
function openDialog() {
opener = document.activeElement // 1. mémoriser
const dlg = document.getElementById('settings')
dlg.hidden = false // 2. afficher
document.querySelectorAll('body > *:not(#settings)')
.forEach(el => el.inert = true) // 3. figer l'arrière-plan
dlg.querySelector('select, button:not([aria-label])')
?.focus() // 4. focus utile
}
function closeDialog() {
const dlg = document.getElementById('settings')
dlg.hidden = true
document.querySelectorAll('[inert]')
.forEach(el => el.inert = false) // 5. rendre la page
opener?.focus() // 6. restituer le focus
}
document.getElementById('open-settings')
.addEventListener('click', openDialog)
document.getElementById('settings')
.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDialog()
if (e.key !== 'Tab') return
// Focus trap : boucler dans le dialog
const items = [...e.currentTarget.querySelectorAll(
'button, select, input, textarea, a[href]')]
const first = items[0], last = items.at(-1)
if (e.shiftKey && document.activeElement === first) {
e.preventDefault(); last.focus()
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault(); first.focus()
}
})Avec inert sur l'arrière-plan, le piège à focus Tab/Shift+Tab devient une sécurité de plus ; le vrai verrou est déjà posé.
#Contraste, zoom et préférences
- Contraste : 4.5:1 minimum pour du texte courant, 3:1 pour du texte grand (18.66px gras ou 24px) et les bordures de composants interactifs. Vérifiez avec le sélecteur de couleur de la DevTools, qui affiche le ratio.
- Zoom : votre page doit rester utilisable à 200 % de zoom et se reflow à 320px de large (une colonne, pas de défilement horizontal). C'est le critère WCAG 1.4.10.
- Mouvement réduit : enveloppez les animations dans
@media (prefers-reduced-motion: reduce)et ne le faites pas qu'en CSS si l'animation vient de JS (matchMedia('(prefers-reduced-motion: reduce)')). - Ordre de lecture : le DOM est la voix du lecteur d'écran. Ne « corrigez » pas l'ordre visuel avec
orderen CSS sans vérifier ce que donne l'ordre du DOM.
#Erreurs fréquentes à éviter
- Des
divcliquables sans rôle ni gestion clavier : utilisezbutton, le travail est déjà fait. - Des placeholders à la place de labels : ils disparaissent à la saisie, ont un contraste faible et ne s'annoncent pas toujours.
- Des messages d'erreur uniquement colorés : ajoutez le texte et le lien
aria-describedby. - Des animations lourdes non désactivables malgré
prefers-reduced-motion. - Supprimer le contour de focus (
outline: none) sans replacement visible : c'est retirer le clignotant d'une voiture.
#Exercice
Reprenez le modal ci-dessus et vérifiez-le en conditions réelles : navigation clavier seule du premier au dernier contrôle, Échap pour fermer, et constat que le focus revient au bouton d'ouverture. Auditez ensuite avec l'onglet Accessibility de la DevTools (nom, rôle, valeur du dialog) puis avec l'extension axe DevTools pour chasser les violations restantes.
Preuve attendue : une liste de trois observations (« Échap ferme et restitue le focus », « aria-modal annoncé », « aucune violation axe critique ») plutôt qu'un simple « ça marche ».