From 876d59f087a3228807fa0318c741c852db6d5351 Mon Sep 17 00:00:00 2001 From: Eric Bouchut Date: Sun, 12 Jul 2026 21:35:26 +0200 Subject: [PATCH 1/3] docs(rgaa): Add the RGAA self-audit report Dossier appendix (French): Lighthouse 13.4.0 and axe-core 4.12.1 run against every page of the running application at dev commit 8c82a93, including the authenticated dashboard (real session) and the register error state. Every page scores 100/100 on Lighthouse and zero axe violations under the WCAG 2.1 A/AA rule set. The manual keyboard walkthrough records the full tab order with a visible 2 px focus ring on all 12 stops, proves the skip link works (the Tab after activating it lands in the form), and completes a keyboard-only login. axe's color-contrast incomplete items are resolved manually against the computed token ratios (worst shipped pair 4.73:1). One robustness recommendation: add tabindex=-1 on main#main so the skip link moves focus explicitly. The reproduction commands are in the report; docs/rgaa.md now links to it and its remaining-passes list shrinks to the VoiceOver smoke test. --- docs/rgaa-audit.md | 167 +++++++++++++++++++++++++++++++++++++++++++++ docs/rgaa.md | 10 ++- 2 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 docs/rgaa-audit.md diff --git a/docs/rgaa-audit.md b/docs/rgaa-audit.md new file mode 100644 index 0000000..63b6376 --- /dev/null +++ b/docs/rgaa-audit.md @@ -0,0 +1,167 @@ +# Auto-audit d'accessibilité RGAA + +Rapport d'auto-audit d'accessibilité de **learn-dev**, destiné à figurer en +annexe du dossier projet de la certification DWWM. Il complète +[docs/rgaa.md](rgaa.md) (English), qui explique chaque critère et où il est +implémenté ; le présent document apporte la **preuve outillée** : mesures +Lighthouse et axe-core sur chaque page, et parcours clavier manuel. + +- **Date de l'audit** : 12 juillet 2026 +- **État audité** : branche `dev`, commit `8c82a93` (application démarrée + localement, profil `dev`) +- **Périmètre** : les 7 pages de l'application, y compris la page + authentifiée, plus l'état d'erreur du formulaire d'inscription +- **Référentiel** : RGAA 4.x (déclinaison française de WCAG 2.1 niveau AA) + +## Méthode et outils + +| Outil | Version | Rôle | +|-------|---------|------| +| Lighthouse | 13.4.0 | Score d'accessibilité par page (Chrome headless) | +| axe-core | 4.12.1 | Analyse statique du DOM rendu, jeu de règles WCAG 2.1 A/AA + bonnes pratiques, injecté dans le navigateur sur la page réelle | +| Parcours clavier | manuel | Ordre de tabulation, visibilité du focus, lien d'évitement, connexion au clavier seul | + +Trois angles indépendants : Lighthouse note la page, axe-core détaille les +règles (y compris celles que Lighthouse ne couvre pas), et le parcours +clavier vérifie ce qu'aucun outil automatique ne sait prouver +(l'utilisabilité réelle au clavier). La page authentifiée `/dashboard` a été +auditée avec une vraie session (compte créé pour l'audit), pas une copie +statique. + +**Exemple : reproduire la mesure Lighthouse d'une page** + +```shell +npx lighthouse "http://localhost:8082/auth/login" \ + --only-categories=accessibility \ + --output=json --output-path=lh-login.json \ + --chrome-flags="--headless=new" +``` + +Où : + +- `--only-categories=accessibility` limite l'audit à la catégorie + accessibilité (les autres catégories ne concernent pas ce rapport) ; +- `--chrome-flags="--headless=new"` exécute Chrome sans interface ; +- le score se lit dans `categories.accessibility.score` du JSON (1 = 100 %). + +**Exemple : reproduire l'analyse axe-core dans le navigateur** + +```js +// Dans la console du navigateur, sur la page à auditer, +// après avoir chargé axe.min.js (npm install axe-core) : +const r = await axe.run(document, { + runOnly: { type: 'tag', + values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice'] } +}); +console.log(r.violations); // [] attendu +``` + +## Résultats : mesures automatiques + +| Page | État | Lighthouse | axe : violations | axe : règles passées | +|------|------|-----------:|-----------------:|---------------------:| +| `/` (accueil) | anonyme | **100** | **0** | 32 | +| `/auth/login` | anonyme | **100** | **0** | 37 | +| `/auth/register` | anonyme | **100** | **0** | 38 | +| `/auth/register` | erreurs de saisie | n/a (voir ci-dessous) | n/a | markup vérifié | +| `/auth/forgot-password` | anonyme | **100** | **0** | 38 | +| `/auth/reset-password` | lien invalide | **100** | **0** | 35 | +| `/privacy` (`lang="fr"`) | anonyme | **100** | **0** | 37 | +| `/dashboard` | **authentifié** | **100** | **0** | 33 | + +**État d'erreur du formulaire d'inscription** : une soumission vide (POST +anonyme avec jeton CSRF valide) re-rend la page (HTTP 200) avec 3 champs +marqués `aria-invalid="true"`, chacun relié à son message par +`aria-describedby` (par exemple `aria-describedby="username-hint +username-error"`, message « must not be blank »). Ce câblage est aussi +verrouillé par un test de non-régression MockMvc +(`AuthFlowTest.register_form_renders_and_shows_field_errors`). + +### Points « incomplete » d'axe-core + +Sur `/auth/login`, axe-core classe 8 nœuds en `incomplete` (ni réussite ni +échec) pour la règle `color-contrast` : le **lien d'évitement**, positionné +hors écran au-dessus de l'en-tête tant qu'il n'a pas le focus, chevauche +géométriquement d'autres éléments, et l'outil renonce à déterminer leur +couleur de fond. Vérification manuelle : tous les couples texte/fond de la +charte sont calculés par script avec la formule de luminance relative WCAG +(méthode et valeurs dans +[design/theme-exploration.md](design/theme-exploration.md)) ; le pire couple +livré est à **4,73:1**, au-dessus du seuil AA de 4,5:1. Aucune action requise. + +## Résultats : parcours clavier + +Réalisé sur `/auth/login` en mode sombre (`prefers-color-scheme: dark`), +puis étendu par sondage aux autres pages (même gabarit `layout.html`, mêmes +styles de focus globaux). + +### Ordre de tabulation constaté + +| Tab | Élément | Focus visible | +|----:|---------|---------------| +| 1 | Lien d'évitement « Skip to main content » | contour 2 px | +| 2 | Marque « learn-dev » (retour accueil) | contour 2 px | +| 3 | Navigation : « Home » | contour 2 px | +| 4 | Navigation : « Log in » | contour 2 px | +| 5 | Navigation : « Sign up » | contour 2 px | +| 6 | Champ « Username » | contour 2 px | +| 7 | Champ « Password » | contour 2 px | +| 8 | Bouton « Log in » | contour 2 px | +| 9 | Lien « Create one » | contour 2 px | +| 10 | Lien « Forgot your password? » | contour 2 px | +| 11 | Pied de page : « Privacy policy (RGPD) » | contour 2 px | +| 12 | Pied de page : « Source on GitHub » | contour 2 px | + +L'ordre suit la lecture visuelle, aucun piège au clavier, et **chacun des 12 +arrêts** affiche l'indicateur de focus global (`outline: 2px solid`, couleur +du jeton bleu, mesuré `rgb(137, 180, 250)` en mode sombre). Aucun +`outline: none` non compensé. + +### Lien d'évitement (RGAA 12.7) + +- Premier élément focusable de chaque page ; visible dès qu'il a le focus. +- Cible `href="#main"` présente (`
`). +- **Preuve d'utilité** : après activation du lien, la tabulation suivante + atterrit directement sur le champ « Username », en sautant les 4 liens + d'en-tête. + +### Connexion au clavier seul + +Le parcours complet de connexion a été réalisé sans souris : lien +d'évitement, saisie de l'identifiant, tabulation, saisie du mot de passe, +activation du bouton de soumission, arrivée sur `/dashboard`. Le formulaire +est un `
` avec un `