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