screen-reader-testing

Par wshobson · agents

Testez les applications web avec des lecteurs d'écran tels que VoiceOver, NVDA et JAWS. À utiliser pour valider la compatibilité avec les lecteurs d'écran, déboguer les problèmes d'accessibilité ou garantir la prise en charge des technologies d'assistance.

npx skills add https://github.com/wshobson/agents --skill screen-reader-testing

Test avec lecteur d'écran

Guide pratique pour tester les applications web avec des lecteurs d'écran afin de valider l'accessibilité de manière exhaustive.

Quand utiliser cette compétence

  • Valider la compatibilité avec les lecteurs d'écran
  • Tester les implémentations ARIA
  • Déboguer les problèmes de technologies d'assistance
  • Vérifier l'accessibilité des formulaires
  • Tester les annonces de contenu dynamique
  • Assurer l'accessibilité de la navigation

Concepts fondamentaux

1. Lecteurs d'écran majeurs

Lecteur d'écran Plateforme Navigateur Utilisation
VoiceOver macOS/iOS Safari ~15%
NVDA Windows Firefox/Chrome ~31%
JAWS Windows Chrome/IE ~40%
TalkBack Android Chrome ~10%
Narrator Windows Edge ~4%

2. Priorité de test

Couverture minimale :
1. NVDA + Firefox (Windows)
2. VoiceOver + Safari (macOS)
3. VoiceOver + Safari (iOS)

Couverture complète :
+ JAWS + Chrome (Windows)
+ TalkBack + Chrome (Android)
+ Narrator + Edge (Windows)

3. Modes des lecteurs d'écran

Mode Objectif Quand l'utiliser
Navigation Lire le contenu Lecture par défaut
Focus/Formulaires Interagir avec les contrôles Remplir les formulaires
Application Widgets personnalisés Applications ARIA

VoiceOver (macOS)

Configuration

Activer : Préférences système → Accessibilité → VoiceOver
Basculer : Cmd + F5
Basculement rapide : Triple-appui sur Touch ID

Commandes essentielles

Navigation :
VO = Ctrl + Option (modificateur VoiceOver)

VO + Flèche droite   Élément suivant
VO + Flèche gauche   Élément précédent
VO + Maj + Bas       Entrer dans un groupe
VO + Maj + Haut      Quitter un groupe

Lecture :
VO + A               Lire tout depuis le curseur
Ctrl                 Arrêter la parole
VO + B               Lire le paragraphe actuel

Interaction :
VO + Espace          Activer l'élément
VO + Maj + M         Ouvrir le menu
Tab                  Prochain élément focalisable
Maj + Tab            Élément focalisable précédent

Rotor (VO + U) :
Naviguer par : Titres, Liens, Formulaires, Points de repère
Flèche gauche/droite Changer la catégorie du rotor
Flèche haut/bas      Naviguer dans la catégorie
Entrée               Aller à l'élément

Spécifique au web :
VO + Cmd + H         Titre suivant
VO + Cmd + J         Contrôle de formulaire suivant
VO + Cmd + L         Lien suivant
VO + Cmd + T         Tableau suivant

Checklist de test

## Checklist de test VoiceOver

### Chargement de la page

- [ ] Titre de la page annoncé
- [ ] Point de repère principal trouvé
- [ ] Lien de saut fonctionnel

### Navigation

- [ ] Tous les titres découvrables via le rotor
- [ ] Niveaux de titre logiques (H1 → H2 → H3)
- [ ] Points de repère correctement étiquetés
- [ ] Liens de saut fonctionnels

### Liens et boutons

- [ ] Objectif du lien clair
- [ ] Actions des boutons décrites
- [ ] Nouvelle fenêtre/onglet annoncé

### Formulaires

- [ ] Tous les libellés lus avec les champs
- [ ] Champs obligatoires annoncés
- [ ] Messages d'erreur lus
- [ ] Instructions disponibles
- [ ] Focus se déplace vers les erreurs

### Contenu dynamique

- [ ] Alertes annoncées immédiatement
- [ ] États de chargement communiqués
- [ ] Mises à jour de contenu annoncées
- [ ] Les modales piègent le focus correctement

### Tableaux

- [ ] En-têtes associés aux cellules
- [ ] Navigation dans le tableau fonctionnelle
- [ ] Les tableaux complexes ont des légendes

Problèmes courants et solutions

<!-- Problème : Bouton n'annonçant pas son objectif -->
<button><svg>...</svg></button>

<!-- Solution -->
<button aria-label="Fermer la boîte de dialogue"><svg aria-hidden="true">...</svg></button>

<!-- Problème : Contenu dynamique non annoncé -->
<div id="results">Nouveaux résultats chargés</div>

<!-- Solution -->
<div id="results" role="status" aria-live="polite">Nouveaux résultats chargés</div>

<!-- Problème : Erreur de formulaire non lue -->
<input type="email" />
<span class="error">E-mail invalide</span>

<!-- Solution -->
<input type="email" aria-invalid="true" aria-describedby="email-error" />
<span id="email-error" role="alert">E-mail invalide</span>

NVDA (Windows)

Configuration

Télécharger : nvaccess.org
Démarrer : Ctrl + Alt + N
Arrêter : Insert + Q

Commandes essentielles

Navigation :
Insert = modificateur NVDA

Flèche bas         Ligne suivante
Flèche haut        Ligne précédente
Tab                Suivant focalisable
Maj + Tab          Précédent focalisable

Lecture :
NVDA + Flèche bas  Tout dire
Ctrl               Arrêter la parole
NVDA + Flèche haut Ligne actuelle

Titres :
H                  Titre suivant
Maj + H            Titre précédent
1-6                Titre niveau 1-6

Formulaires :
F                  Champ de formulaire suivant
B                  Bouton suivant
E                  Champ d'édition suivant
X                  Case à cocher suivante
C                  Zone de liste modifiable suivante

Liens :
K                  Lien suivant
U                  Lien non visité suivant
V                  Lien visité suivant

Points de repère :
D                  Point de repère suivant
Maj + D            Point de repère précédent

Tableaux :
T                  Tableau suivant
Ctrl + Alt + Flèches Naviguer les cellules

Liste des éléments (NVDA + F7) :
Affiche tous les liens, titres, champs de formulaire, points de repère

Mode Navigation vs Mode Focus

NVDA bascule automatiquement les modes :
- Mode Navigation : Les touches fléchées naviguent le contenu
- Mode Focus : Les touches fléchées contrôlent les éléments interactifs

Basculement manuel : NVDA + Espace

À surveiller :
- Annonce « Mode Navigation » lors de la navigation
- « Mode Focus » lors de l'entrée dans des champs de formulaire
- Le rôle Application force le mode formulaires

Script de test

## Script de test NVDA

### Chargement initial

1. Naviguer vers la page
2. Laisser la page se charger complètement
3. Appuyer sur Insert + Down pour tout lire
4. Remarquer : Titre de la page, contenu principal identifiés ?

### Navigation par points de repère

1. Appuyer sur D à plusieurs reprises
2. Vérifier : Toutes les zones principales accessibles ?
3. Vérifier : Points de repère correctement étiquetés ?

### Navigation par titres

1. Appuyer sur Insert + F7 → Titres
2. Vérifier : Structure de titre logique ?
3. Appuyer sur H pour naviguer les titres
4. Vérifier : Toutes les sections découvrables ?

### Test de formulaire

1. Appuyer sur F pour trouver le premier champ de formulaire
2. Vérifier : Libellé lu ?
3. Saisir des données invalides
4. Soumettre le formulaire
5. Vérifier : Erreurs annoncées ?
6. Vérifier : Focus déplacé vers l'erreur ?

### Éléments interactifs

1. Parcourir par Tab tous les éléments interactifs
2. Vérifier : Chacun annonce son rôle et son état
3. Activer les boutons avec Entrée/Espace
4. Vérifier : Résultat annoncé ?

### Contenu dynamique

1. Déclencher une mise à jour de contenu
2. Vérifier : Changement annoncé ?
3. Ouvrir une modale
4. Vérifier : Focus piégé ?
5. Fermer la modale
6. Vérifier : Focus renvoyé ?

JAWS (Windows)

Commandes essentielles

Démarrer : Raccourci bureau ou Ctrl + Alt + J
Curseur virtuel : Activé automatiquement dans les navigateurs

Navigation :
Touches fléchées   Naviguer le contenu
Tab                Suivant focalisable
Insert + Down      Tout dire
Ctrl               Arrêter la parole

Touches rapides :
H                  Titre suivant
T                  Tableau suivant
F                  Champ de formulaire suivant
B                  Bouton suivant
G                  Graphique suivant
L                  Liste suivante
;                  Point de repère suivant

Mode Formulaires :
Entrée             Entrer mode formulaires
Numpad +           Quitter mode formulaires
F5                 Liste des champs de formulaire

Listes :
Insert + F7        Liste de liens
Insert + F6        Liste de titres
Insert + F5        Liste de champs de formulaire

Tableaux :
Ctrl + Alt + Flèches Navigation dans le tableau

TalkBack (Android)

Configuration

Activer : Paramètres → Accessibilité → TalkBack
Basculer : Maintenir les deux boutons de volume 3 secondes

Gestes

Explorer : Faire glisser le doigt sur l'écran
Suivant : Balayer vers la droite
Précédent : Balayer vers la gauche
Activer : Double-appui
Faire défiler : Balayage à deux doigts

Contrôles de lecture (balayage vers le haut puis vers la droite) :
- Titres
- Liens
- Contrôles
- Caractères
- Mots
- Lignes
- Paragraphes

Scénarios de test courants

1. Boîte de dialogue modale

<!-- Structure de modale accessible -->
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-desc"
>
  <h2 id="dialog-title">Confirmer la suppression</h2>
  <p id="dialog-desc">Cette action ne peut pas être annulée.</p>
  <button>Annuler</button>
  <button>Supprimer</button>
</div>
// Gestion du focus
function openModal(modal) {
  // Mémoriser le dernier élément focalisé
  lastFocus = document.activeElement;

  // Déplacer le focus vers la modale
  modal.querySelector("h2").focus();

  // Piéger le focus
  modal.addEventListener("keydown", trapFocus);
}

function closeModal(modal) {
  // Rendre le focus
  lastFocus.focus();
}

function trapFocus(e) {
  if (e.key === "Tab") {
    const focusable = modal.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
    );
    const first = focusable[0];
    const last = focusable[focusable.length - 1];

    if (e.shiftKey && document.activeElement === first) {
      last.focus();
      e.preventDefault();
    } else if (!e.shiftKey && document.activeElement === last) {
      first.focus();
      e.preventDefault();
    }
  }

  if (e.key === "Escape") {
    closeModal(modal);
  }
}

2. Régions actives

<!-- Messages de statut (poli) -->
<div role="status" aria-live="polite" aria-atomic="true">
  <!-- Les mises à jour de contenu seront annoncées après la parole actuelle -->
</div>

<!-- Alertes (assertif) -->
<div role="alert" aria-live="assertive">
  <!-- Les mises à jour de contenu interrompent la parole actuelle -->
</div>

<!-- Mises à jour de progression -->
<div
  role="progressbar"
  aria-valuenow="75"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-label="Progression de l'envoi"
></div>

<!-- Journal (ajouts uniquement) -->
<div role="log" aria-live="polite" aria-relevant="additions">
  <!-- Nouveaux messages annoncés, suppressions non annoncées -->
</div>

3. Interface à onglets

<div role="tablist" aria-label="Informations produit">
  <button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
    Description
  </button>
  <button
    role="tab"
    id="tab-2"
    aria-selected="false"
    aria-controls="panel-2"
    tabindex="-1"
  >
    Avis
  </button>
</div>

<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  Contenu de la description du produit...
</div>

<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  Contenu des avis...
</div>
// Navigation au clavier pour les onglets
tablist.addEventListener("keydown", (e) => {
  const tabs = [...tablist.querySelectorAll('[role="tab"]')];
  const index = tabs.indexOf(document.activeElement);

  let newIndex;
  switch (e.key) {
    case "ArrowRight":
      newIndex = (index + 1) % tabs.length;
      break;
    case "ArrowLeft":
      newIndex = (index - 1 + tabs.length) % tabs.length;
      break;
    case "Home":
      newIndex = 0;
      break;
    case "End":
      newIndex = tabs.length - 1;
      break;
    default:
      return;
  }

  tabs[newIndex].focus();
  activateTab(tabs[newIndex]);
  e.preventDefault();
});

Astuces de débogage

// Enregistrer ce que le lecteur d'écran voit
function logAccessibleName(element) {
  const computed = window.getComputedStyle(element);
  console.log({
    role: element.getAttribute("role") || element.tagName,
    name:
      element.getAttribute("aria-label") ||
      element.getAttribute("aria-labelledby") ||
      element.textContent,
    state: {
      expanded: element.getAttribute("aria-expanded"),
      selected: element.getAttribute("aria-selected"),
      checked: element.getAttribute("aria-checked"),
      disabled: element.disabled,
    },
    visible: computed.display !== "none" && computed.visibility !== "hidden",
  });
}

Bonnes pratiques

À faire

  • Tester avec les vrais lecteurs d'écran - Pas seulement des simulateurs
  • Utiliser d'abord du HTML sémantique - ARIA est supplémentaire
  • Tester en mode Navigation et mode Focus - Expériences différentes
  • Vérifier la gestion du focus - En particulier pour les SPA
  • Tester d'abord au clavier seul - Fondation pour le test avec lecteur d'écran

À ne pas faire

  • Ne pas supposer qu'un seul lecteur d'écran suffit - Tester plusieurs
  • Ne pas ignorer mobile - Base d'utilisateurs croissante
  • Ne pas tester uniquement le chemin heureux - Tester les états d'erreur
  • Ne pas sauter le contenu dynamique - Problèmes les plus courants
  • Ne pas se fier au test visuel - Expérience différente

Skills similaires