Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

13.4 — Accessibilité avancée

🎯 Objectif : livrer des interfaces utilisables au clavier, par lecteurs d’écran, par malvoyants, par moteurs cognitifs variés. Ce n’est pas une option en 2026 (RGAA, ADA, EAA), c’est une exigence.

À l'issue de cet axe, tu sauras :

  • Comprendre les 4 principes WCAG (POUR) et les niveaux A / AA / AAA
  • Combiner tests automatiques (axe-core, Lighthouse) et tests manuels (clavier, lecteur d'écran)
  • Implémenter les patterns ARIA des composants courants : modale, menu, tabs, combobox
  • Respecter prefers-reduced-motion et prefers-color-scheme
  • Mener un audit RGAA / WCAG sur une app et écrire un rapport actionnable

Confirmé 12 min prérequis : axes 5-9 lus

Le contexte 2026 — l’accessibilité n’est plus optionnelle

Section intitulée « Le contexte 2026 — l’accessibilité n’est plus optionnelle »

Trois forces de fond qui rendent le sujet incontournable :

ForceConséquence
European Accessibility Act (EAA) — entré en vigueur 28 juin 2025E-commerce, banque, transport, e-learning : conformité obligatoire dans l’UE
WCAG 2.2 (publié oct. 2023, intégré aux normes nationales)9 nouveaux critères (focus visible, drag, target size, etc.)
Procès ADA aux US, RGAA 4.1 en FranceRisque légal réel pour les services publics et grandes entreprises

Conclusion pratique : si tu codes du logiciel client en 2026 sans pratique a11y, tu prends un risque juridique pour ton employeur ou client.


WCAG 2.2 organise tous ses critères autour de 4 principes :

PrincipeSensExemples concrets
PerceptibleL’info doit être perçue par tousAlt sur images, captions vidéo, contraste, taille texte
OpérableTout doit fonctionner sans sourisNavigation clavier, raccourcis, target size 24×24 px
Utilisable / CompréhensibleL’interaction doit être prévisibleLang attribute, labels, messages d’erreur clairs
RobusteCode valide, compatible AT (Assistive Tech)HTML sémantique, ARIA correct, parsing valide
NiveauCibleEn pratique
AMinimum vital — sans, le site est inutilisable par certainsDoit être atteint, toujours
AAExigence légale standard (EAA, RGAA, ADA)Cible normale des projets pro
AAAExigeant, parfois impossible (sous-titrage live, contraste 7:1)Cas où tu veux exceller, pas une cible globale

Vise AA systématiquement. Le AAA partiel selon les composants critiques.

CritèreNiveauQuoi
2.4.11 Focus Not ObscuredAALe focus visible n’est jamais masqué (par sticky header, par exemple)
2.5.7 Dragging MovementsAAToute action drag doit avoir une alternative non-drag
2.5.8 Target SizeAACible interactive ≥ 24×24 px (sauf cas inline)
3.2.6 Consistent HelpALes liens d’aide à la même position partout
3.3.7 Redundant EntryANe pas redemander une info déjà donnée dans le tunnel
3.3.8 Accessible AuthenticationAAPas de puzzles cognitifs (« retape ton mot de passe sans coller »)

Tests automatiques vs manuels — combiner les deux

Section intitulée « Tests automatiques vs manuels — combiner les deux »

Les tests automatiques attrapent ~30 à 40 % des problèmes WCAG. Le reste demande l’œil humain.

OutilTypeQuand l’utiliser
axe-core (DevTools, Playwright, Cypress)Audit pageEn CI sur chaque PR
Lighthouse a11yAudit pageVue d’ensemble
eslint-plugin-jsx-a11yLint à l’écritureAu moment du dev
pa11yCLI / CIAudits de masse multi-pages
Storybook a11y addonComposant unitaireEn développement de design system

Exemple Playwright + axe :

import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('home is a11y compliant', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toHaveLength(0);
});
  1. Naviguer au clavier seul (Tab, Shift+Tab, Enter, Escape, flèches).
  2. Utiliser un lecteur d’écran : NVDA (Windows, gratuit), VoiceOver (Mac/iOS, intégré), TalkBack (Android).
  3. Zoomer à 200 % : les contenus restent lisibles, pas de scroll horizontal.
  4. Désactiver les CSS (DevTools : Disable styles) : la structure HTML reste-t-elle compréhensible ?
  5. Utiliser le mode contraste élevé Windows.
  6. Tester avec prefers-reduced-motion: reduce.

L’erreur la plus courante : tout faire avec <div> et empiler des ARIA pour compenser.

<!-- ❌ -->
<div class="button" onclick="...">Soumettre</div>
<!-- ✅ Le navigateur fournit gratuitement : focus, role, clavier, lecteur d'écran -->
<button type="submit">Soumettre</button>
<header>...</header> <!-- = role banner -->
<nav>...</nav> <!-- = role navigation -->
<main>...</main> <!-- = role main, le contenu unique de la page -->
<aside>...</aside> <!-- = role complementary -->
<footer>...</footer> <!-- = role contentinfo -->

Un lecteur d’écran propose alors « aller au contenu principal » via D — quasi gratuit.

<!-- ✅ Hiérarchie : 1 seul h1, h2 sous le h1, h3 sous h2 -->
<h1>Mon site</h1>
<h2>Articles récents</h2>
<h3>Premier article</h3>
<h3>Deuxième article</h3>
<h2>Newsletter</h2>

Pas de saut (h1 → h3 directement), pas de h1 multiples par page.


« No ARIA is better than bad ARIA » — W3C

Avant d’écrire role ou aria-*, demande-toi : est-ce qu’un élément HTML standard ne suffirait pas ? 90 % du temps, oui.

Tu veuxHTML standard suffit
Un bouton<button>
Un lien<a href>
Une case à cocher<input type="checkbox">
Un onglet ouvrable<details><summary>

ARIA devient nécessaire pour les patterns sans équivalent HTML : combobox, tabs, tree, dialog, menu, etc.

<dialog id="dlg" aria-labelledby="dlg-title">
<h2 id="dlg-title">Confirmer la suppression</h2>
<p>Cette action est irréversible.</p>
<button autofocus>Annuler</button>
<button>Supprimer</button>
</dialog>
const dlg = document.getElementById('dlg');
dlg.showModal(); // bloque le focus sur la modale, gère Escape

L’élément <dialog> natif (universel en 2026) gère tout : focus trap, Escape, aria-modal="true", retour de focus.

Les patterns ARIA sont complexes et peu intuitifs. Utilise une lib éprouvée plutôt que de les réécrire :

PatternLib recommandée
Combobox / Listbox / Menu / Tabs / DialogRadix UI (React)
Idem VueReka UI (anciennement Radix Vue)
Idem SvelteBits UI ou Melt UI
Headless universelHeadless UI (Tailwind Labs)

Ces librairies sont headless : zéro style imposé, accessibilité gérée. Économie monumentale.

Pour annoncer une mise à jour dynamique au lecteur d’écran :

<div role="status" aria-live="polite">
Panier mis à jour : 3 articles
</div>
aria-liveQuand
politeAnnonce dès que possible, sans interrompre
assertiveInterrompt immédiatement (réservé aux erreurs critiques)
Anti-patternPourquoi mauvais
role="button" sur un <div>Manque le clavier, le focus, l’état actif
aria-hidden="true" sur un élément focusableConfusion : le screen reader skip mais l’utilisateur clavier l’atteint
aria-label redondant avec le texte visibleBruit pour le lecteur d’écran
tabindex="0" partoutL’ordre de tab devient absurde
tabindex="5" (ou autre nombre > 0)À NE JAMAIS faire — détourne l’ordre naturel

<label for="email">Adresse email</label>
<input id="email" name="email" type="email"
autocomplete="email" required
aria-describedby="email-help email-err"
aria-invalid="true">
<small id="email-help">On ne le partage avec personne.</small>
<small id="email-err" role="alert">Format invalide.</small>
Bonne pratiquePourquoi
<label for> lié à <input id>Clic sur label active l’input, lecteur d’écran annonce
autocomplete correctRemplissage navigateur + lecteurs d’écran
aria-describedbyDescription et erreur lues
aria-invalid + role="alert"L’erreur est annoncée en live
Type spécifique (email, tel, number)Clavier mobile adapté
inputmode="numeric"Précise le clavier pour PIN, OTP
Erreurs textuellesPas seulement « bordure rouge »

ÉlémentNiveau AA
Texte normal4.5:1
Texte large (≥ 18.66 px bold ou 24 px normal)3:1
UI (bordures focus, icônes informatives)3:1
  • Chrome DevTools → Inspector → couleur du texte → l’outil affiche le ratio.
  • Stark (extension navigateur) → audit complet.
  • APCA (Accessible Perceptual Contrast Algorithm) → futur successeur du WCAG 2.x ratio, plus juste perceptivement.
<!-- ❌ Le rouge seul ne suffit pas pour un daltonien -->
<input class="error" />
<!-- ✅ Couleur + icône + texte -->
<input class="error" aria-invalid="true" />
<span role="alert">⚠ Email invalide</span>

Certains utilisateurs (vestibulaires, neurodivergents) ressentent du malaise face aux animations.

@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduce) {
// animation décorative
}
Animation typeReduce →
Décorative (parallax, fade)Désactiver
Fonctionnelle (transition entre états)Réduire la durée mais garder
Auto-play vidéoDésactiver (WCAG 2.2.2)
Media queryQuoi
prefers-color-scheme: darkThème sombre
prefers-contrast: moreAugmenter le contraste
prefers-reduced-transparencyDésactiver les flous
prefers-reduced-dataServir moins de média

WCAG 2.2 critère 2.5.8 : 24×24 px minimum pour tout élément interactif (sauf inline dans un paragraphe).

button, a {
min-height: 24px;
min-width: 24px;
}

44×44 px est le standard Apple HIG, encore plus confortable. Iconique pour les boutons mobile.


/* ✅ Focus visible uniquement quand on navigue au clavier */
:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
/* ❌ Ne JAMAIS faire :focus { outline: none } sans alternative */

WCAG 2.4.11 : ce focus ne doit pas être caché par un sticky header, une banière fixe, etc. Test : Tab à travers la page et vérifie que le focus reste toujours visible.


L’attribut lang fait que le lecteur d’écran prononce correctement :

<html lang="fr">
...
<p>Le mot <span lang="en">framework</span> vient de l'anglais.</p>
</html>

Sans lang, NVDA va lire « framework » avec une prononciation française très étrange.


Tu lances axe-core sur ton site, 0 violation. Peux-tu déclarer le site WCAG AA conforme ?
Tu codes une modale `<div role='dialog' aria-modal='true'>` avec ton focus trap maison. Quelle alternative 2026 t'évite la moitié du travail ?
Pour un texte rouge sur fond gris clair, le contraste est de 3.8:1. Niveau WCAG AA pour du texte normal ?
Un utilisateur active prefers-reduced-motion: reduce. Comment dois-tu adapter une transition de hover sur un bouton (200 ms) et une animation de parallax background ?


Suite : 13.5 — Internationalisation pour rendre l’application multilingue et culturellement adaptée.