// Tests & qualité

Tester l'accessibilité de son code : axe, Lighthouse et lecteur d'écran

Un bouton sans nom accessible, un champ sans étiquette, un contraste trop faible : ces défauts passent sans bruit une revue de code classique et finissent en production. Pourtant, une bonne partie d’entre eux se détecte automatiquement, avec les mêmes réflexes que des tests unitaires. Ce tutoriel installe deux garde-fous dans votre pipeline, axe-core via Playwright et Lighthouse CI, puis montre pourquoi ils ne suffisent pas et comment compléter avec un lecteur d’écran.

Pourquoi tester l’accessibilité comme le reste du code

L’accessibilité se dégrade par petites touches. Un composant refactoré perd son aria-label, une nouvelle modale ne rend plus le focus, un designer éclaircit un gris. Sans test, personne ne s’en aperçoit avant qu’un utilisateur bloqué ne le signale.

Le contexte réglementaire rend le sujet moins optionnel qu’avant. En France, le référentiel de référence est le RGAA 4.1.2 : 106 critères répartis en 13 thématiques. Depuis le 28 juin 2025, l’European Accessibility Act étend les obligations à de nombreux services privés (e-commerce, banque, transport), avec l’ARCOM chargée des contrôles. Pour un développeur, la conséquence est simple : l’accessibilité devient une exigence testable, comme la sécurité ou la performance.

Étape 1 : axe-core dans vos tests Playwright

axe-core est le moteur d’analyse open source de Deque. Il inspecte le DOM rendu et remonte les violations en les rattachant aux critères WCAG. Avec Playwright, l’intégration tient en quelques lignes.

npm install -D @playwright/test @axe-core/playwright
npx playwright install chromium

Un premier test qui échoue dès qu’une violation est détectée sur l’une des pages clés :

// tests/a11y.spec.js
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

const pages = ['/', '/contact/', '/blog/'];

for (const path of pages) {
  test(`accessibilité : ${path}`, async ({ page }) => {
    await page.goto(path);
    const results = await new AxeBuilder({ page })
      .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
      .analyze();
    expect(results.violations).toEqual([]);
  });
}

Quelques conseils pour que ce test reste utile :

  • Testez les états, pas seulement les pages. Ouvrez le menu mobile, déclenchez une erreur de formulaire, affichez la modale, puis lancez l’analyse : axe ne voit que le DOM présent au moment de l’appel.
  • Ciblez avec .include() et .exclude() pour isoler un composant ou écarter un widget tiers que vous ne maîtrisez pas, en le documentant.
  • Lisez violations[].nodes : chaque entrée donne le sélecteur fautif et un lien vers l’explication de la règle. C’est ce qu’il faut afficher dans les logs de CI.

Étape 2 : Lighthouse CI comme filet de sécurité

Lighthouse, intégré à Chrome, calcule un score d’accessibilité sur 100. Ses audits reposent en grande partie sur axe-core, donc il ne trouvera pas grand-chose de plus. Son intérêt est ailleurs : suivre une tendance et bloquer une régression nette, en même temps que la performance et le SEO.

// lighthouserc.json
{
  "ci": {
    "collect": { "staticDistDir": "./dist" },
    "assert": {
      "assertions": {
        "categories:accessibility": ["error", { "minScore": 0.95 }]
      }
    }
  }
}

Puis dans le workflow, après le build :

- name: Tests axe (Playwright)
  run: npx playwright test tests/a11y.spec.js

- name: Lighthouse CI
  run: npx @lhci/cli autorun

Si la syntaxe des workflows vous est nouvelle, le guide GitHub Actions détaille les jobs, les déclencheurs et le cache npm. L’approche rejoint celle des tests unitaires avec Jest : un test rouge bloque la fusion, et la correction se fait avant la mise en production.

Attention au piège du score : un 100 sur 100 dans Lighthouse signifie seulement qu’aucun audit automatique n’a échoué. Ce n’est pas une attestation de conformité.

Ce que les robots ne voient pas

Les outils automatiques couvrent environ un tiers des critères du RGAA. Le reste demande un jugement humain, parce que la question n’est pas « l’attribut existe-t-il ? » mais « a-t-il du sens ? ». Le tableau ci-dessous résume la frontière.

VérificationAutomatisablePourquoi
Image sans attribut altOuiPrésence ou absence d’un attribut
Alternative textuelle pertinenteNonalt="image" passe le test mais n’aide personne
Contraste d’un texte sur fond uniOuiCalcul de ratio
Contraste d’un texte sur une photoPartiellementLe fond varie, l’outil signale un doute
Ordre de tabulation logiqueNonIl faut parcourir la page
Focus visible et jamais piégéNonComportement dynamique
Hiérarchie de titres cohérentePartiellementUn saut de niveau se détecte, un titre trompeur non
Annonce d’un message d’erreurNonDépend de ce que restitue le lecteur d’écran

Pour savoir quel outil couvre quoi entre extensions, moteurs et validateurs, ce panorama des outils d’audit RGAA classe les solutions selon ce qu’elles vérifient réellement, ce qui aide à ne pas empiler trois outils qui testent la même chose.

Étape 3 : le test manuel, clavier puis lecteur d’écran

Dix minutes par fonctionnalité suffisent souvent à trouver ce que la CI a laissé passer.

Au clavier d’abord. Débranchez la souris et parcourez le parcours critique avec Tab, Maj+Tab, Entrée, Espace et Échap. Vérifiez que chaque élément interactif est atteignable, que le focus reste visible, qu’une modale capture le focus puis le rend à l’élément déclencheur à la fermeture.

Au lecteur d’écran ensuite. Utilisez celui de votre système :

  • NVDA sous Windows, gratuit et open source ;
  • VoiceOver sous macOS, activable avec Cmd+F5 ;
  • Orca sous Linux, fourni avec GNOME ;
  • TalkBack et VoiceOver sur mobile.

Écoutez ce qui est annoncé en naviguant par titres et par liens. Un bouton icône annoncé « bouton » sans autre précision, un lien « cliquez ici » hors contexte, un message d’erreur jamais lu : ce sont exactement les défauts qu’axe ne peut pas juger.

Une routine réaliste pour une équipe

L’objectif n’est pas de transformer chaque développeur en auditeur, mais de répartir l’effort :

  1. À chaque commit : axe via Playwright sur les pages et états clés, bloquant.
  2. À chaque fusion sur main : Lighthouse CI avec un seuil, pour la tendance.
  3. À chaque nouvelle fonctionnalité : passage clavier et lecteur d’écran par le développeur, noté dans la pull request.
  4. Ponctuellement : un audit complet sur un échantillon de pages, grille RGAA en main, pour mesurer le taux de conformité (critères conformes divisés par critères applicables).

Les deux premières étapes se mettent en place une fois pour toutes. Les deux suivantes sont celles qui rendent réellement un site utilisable par tous.