Les messages de statut
« Enregistré », « 3 résultats », « Champ obligatoire » : ces messages apparaissent sans changer de page ni prendre le focus. Ils ne s'entendent que si la région qui les accueille existait déjà, vide. Démo, code, critères RGAA.
3 critères RGAA en jeu
Essayez-les
Le bouton suivant fait la faute exprès : la région et son texte arrivent d’un seul geste.
Activez « Enregistrer l’audit » : le message apparaît, le focus ne bouge pas, et un lecteur d’écran le lit sans que vous ayez rien à chercher. Activez ensuite le bouton qui fait la faute exprès — même rôle, même balisage — et silence, à chaque fois. Le témoin départage : il date chaque région à son entrée dans le document, et c’est le seul écart entre les deux.
Cette démo existe aussi en page autonome.
L’erreur courante
Le message est fabriqué au moment où on veut l’annoncer, rôle compris.
// À ne pas faire : la région et son texte arrivent d'un seul geste
const message = document.createElement('p');
message.setAttribute('role', 'status');
message.textContent = 'Audit enregistré.';
document.querySelector('.entete').append(message); Le balisage est juste, et pourtant on n’entend rien.
Un lecteur d’écran ne lit pas les régions d’annonce : il surveille les changements dans celles qu’il connaît déjà. La région doit donc être entrée dans l’arbre d’accessibilité avant le message, pour qu’il reste ensuite quelque chose à observer. Insérée avec son texte, elle n’offre aucun changement : elle arrive déjà remplie, et personne ne la regardait.
La correction tient en deux lignes déplacées : le conteneur au premier rendu, vide ; le texte plus tard.
<p role="status"></p> document.querySelector('[role="status"]').textContent = 'Audit enregistré.'; Trois variantes coûtent la même chose :
- La région masquée par
display: noneouhidden, puis démasquée. Le conteneur existe dans le document, mais son contenu est hors de l’arbre d’accessibilité tant qu’il est masqué : le démasquer équivaut à le créer. Une région d’annonce reste rendue en permanence — vide, elle n’occupe rien. - Le même texte réécrit. « 0 résultat » deux fois de suite ne change rien dans le document, donc rien n’est annoncé la seconde fois. L’utilisateur croit que sa recherche n’a pas été prise en compte.
- Une région invisible qui double un message déjà visible. Le texte est
annoncé deux fois : une fois par la région
sr-only, une fois à la lecture du message affiché.
Le code, pas à pas
1. La région d’abord, le message ensuite
<!-- Dans le rendu initial de la page, vide et visible -->
<p role="status" class="confirmation"></p> C’est la règle qui rate le plus souvent, et la seule qui ne se voit pas à la
relecture du code : au moment où l’on écrit textContent = …, tout paraît
correct.
Écrivez donc la région dans le gabarit, pas dans le gestionnaire d’événement. Avec un moteur de rendu — Svelte, React, Vue —, cela veut dire un élément rendu en permanence dont le contenu varie, jamais un bloc conditionnel qui apparaît et disparaît.
Une seule région par usage suffit : elle sert tous les messages de même nature, les uns après les autres.
2. role="status" porte déjà l’essentiel
role="status" équivaut à aria-live="polite" et aria-atomic="true" : la
région attend une pause dans le discours, et se relit en entier quand elle
change. Vous n’avez pas à écrire ces deux attributs en plus.
aria-live="polite" seul se justifie quand aucun rôle ne convient — une zone qui
se met à jour sans être un message. Mais sans aria-atomic="true", seul le nœud
modifié est relu : « 3 » au lieu de « 3 résultats ».
Le référentiel n’exige pas qu’un message de statut soit visible : 7.5 ne demande que le rôle, et le mot « visible » ne figure que dans les tests de formulaire 11.10. C’est une bonne pratique, et une bonne pratique forte : un texte réservé aux lecteurs d’écran laisse sans information qui voit mal, qui zoome, ou qui regarde ailleurs au mauvais moment.
3. Quel rôle pour quel message : le critère le dit lui-même
Le critère 7.5 se décompose en trois tests, et chacun désigne son rôle.
| Ce que le message dit | Le rôle attendu | Test |
|---|---|---|
| La réussite, le résultat d’une action, l’état d’une application | role="status" | 7.5.1 |
| Une suggestion, ou l’existence d’une erreur | role="alert" | 7.5.2 |
| La progression d’un processus | role="log", role="progressbar" ou role="status" | 7.5.3 |
« 12 critères enregistrés » relève du premier, « l’adresse est invalide » du
deuxième, « page 3 sur 8 analysée » du troisième. Pour la progression, l’élément
natif <progress> porte role="progressbar" sans rien déclarer — mais il faut
lui donner un nom accessible, sans quoi on entend une valeur sans savoir de quoi.
4. assertive interrompt : ce que le référentiel impose, et ce qui reste au jugement
role="alert" vaut aria-live="assertive" : le lecteur d’écran coupe la
phrase en cours pour lire le message.
Le test 7.5.2 impose ce rôle — donc assertive — pour toute suggestion et
toute erreur, sans condition d’urgence. Une suggestion n’invalide rien et
n’appelle aucune réaction immédiate ; elle relève quand même de role="alert".
Il n’y a rien à arbitrer là.
Ailleurs — les compteurs, les états, la progression, ce que 7.5.1 et 7.5.3
rangent sous status ou log —, réserver assertive à ce qui invalide
l’action en cours est une bonne pratique, pas une exigence du référentiel.
Employé pour « Enregistré », il hache l’écoute sans rien apporter. Et sur un
compteur mis à jour à chaque frappe, il produit un bavardage ininterrompu :
chaque caractère saisi coupe l’annonce précédente, et l’utilisateur n’entend
jamais le résultat complet. polite laisse finir la phrase en cours, puis lit la
dernière valeur.
Hors des cas que 7.5.2 range sous role="alert", si le message n’appelle pas une
réaction immédiate, il est polite.
5. Ne déplacez pas le focus dessus
Le glossaire du référentiel est explicite : « Un message de statut informe l’utilisateur d’un changement de contenu dans la page sans interrompre son activité principale (il n’y a pas de changement de contexte par exemple un repositionnement du focus sur le message). »
Déplacer le focus vers le message est donc un autre composant, avec d’autres règles. Deux conséquences :
- Le message ne relève plus du 7.5, et n’a plus besoin de
role="alert"— le déplacement du focus provoque déjà la restitution. Cumuler les deux fait annoncer deux fois. - Le déplacement est un changement de contexte, que le critère 7.4 encadre : il doit être initié par un bouton ou un lien explicite, ou annoncé avant. Un message qui prend le focus pendant que l’utilisateur saisit — sauvegarde automatique, réponse tardive du serveur — lui fait perdre sa place.
Quand la réponse à un message demande vraiment d’agir tout de suite, c’est une modale qu’il faut, pas une région d’annonce.
6. Un message d’erreur de formulaire doit encore dire quoi corriger
Être entendu et être utile sont deux exigences distinctes. role="alert" règle
la première ; le critère 11.10 pose la seconde : le message doit identifier
nommément le champ concerné, ou le champ porter aria-invalid="true" — et dans
ce cas le message doit être dans son étiquette ou dans le passage de texte qui
lui est associé.
<label for="adresse">Adresse de la page</label>
<input id="adresse" aria-invalid="true" aria-describedby="adresse-erreur" />
<p id="adresse-erreur">L'adresse doit commencer par https://</p> Le détail de ce nouage appartient aux fiches des champs de formulaire et du récapitulatif d’erreurs.
7. Faut-il vraiment qu’il disparaisse ?
Un message qui s’efface au bout de quelques secondes suppose qu’on regardait au bon endroit au bon moment. À 400 % de zoom, il tombe souvent hors de l’écran ; à la loupe, il est parti quand on y arrive ; au lecteur d’écran, l’annonce a pu attendre la fin d’une phrase et le texte n’est plus là quand on veut le relire.
Le référentiel ne fixe pas de durée par un critère dédié : c’est une bonne pratique, pas une exigence. Elle est solide. Un message posé dans le flux, près de l’action qui l’a produit, et qui reste jusqu’à l’action suivante, ne coûte rien à personne.
Au clavier
Un message de statut n’est pas interactif : il n’y a rien à y faire au clavier, et c’est précisément ce qui le définit.
| Touche | Effet |
|---|---|
| Tab | Ne rencontre jamais la région : elle n’est pas focalisable, et n’a pas à l’être. |
| Entrée / Espace (sur l’action) | Déclenche l’action. Le message est annoncé, le focus reste sur le bouton. |
| Échap | N’a rien à fermer. S’il faut pouvoir fermer, c’est une modale, pas un message de statut. |
| Lecture au curseur virtuel | Le message se relit dans le flux de la page — à condition qu’il n’ait pas disparu entre-temps. |
Ce que le lecteur d’écran restitue
Le contenu de la région, et lui seul : « Audit enregistré — 12 critères, 2 non conformes. ». Ni nom, ni rôle, ni annonce d’ouverture — une région de statut ne se présente pas, elle lit ce qui vient de changer en elle. La formulation varie d’un lecteur à l’autre ; ce qui est lu, non.
Le diagnostic inverse tient en quatre constats :
- Rien n’est annoncé : la région a été créée avec son texte, ou elle était
masquée par
display: none, ou le texte est identique au précédent. - Le message est annoncé deux fois : deux régions portent le même texte, ou
le focus a été déplacé sur une région qui porte en plus
role="alert". - La phrase en cours est coupée :
assertivesur un message que 7.5.2 ne range pas sousrole="alert", là oùpolitesuffisait. - Vous n’entendez que le fragment modifié — « 3 » sans « résultats » : la
région est un
aria-livesansaria-atomic="true".
Checklist
- La région dans le rendu initial, vide, jamais créée au moment de l’annonce.
- Jamais
display: nonenihiddensur une région d’annonce. role="status"pour un résultat,role="alert"pour une erreur ou une suggestion.- Hors suggestion et erreur,
assertiveréservé à ce qui invalide l’action en cours. - Un texte qui change vraiment : le même message réécrit ne s’annonce pas.
- Le message visible à l’écran (bonne pratique), et annoncé une seule fois.
- Le focus ne bouge pas — sinon ce n’est plus un message de statut.
Les critères RGAA en jeu
- Critère 7.5 — Messages de statut
Le critère nomme trois familles de messages et le rôle qui va avec : un message qui apparaît hors de toute région d'annonce n'existe que pour l'œil, et rien dans la page ne le signale.
- Critère 7.4 — Changement de contexte annoncé
Le réflexe pour faire entendre un message est d'y déplacer le focus. C'est un changement de contexte, et quand rien de demandé ne l'a provoqué — sauvegarde automatique, résultat qui revient — il faut avertir avant ou y renoncer.
- Critère 11.10 — Contrôle de saisie
Une erreur de saisie annoncée par role=alert a franchi une exigence, pas deux : le message doit encore identifier le champ concerné, ou le champ porter aria-invalid et son message associé.
Vérifiez votre composant avec l'extension
Pour aller plus loin
- Critère 7.5 — Messages de statut : les trois tests, et ce qu’un audit déclenche pour les vérifier.
- « Message de statut » au glossaire : la définition qui décide si votre message en est un.
- Le récapitulatif d’erreurs : le cas où le focus se déplace, et où le 7.5 cesse donc de s’appliquer.
- Le champ de recherche : un compteur de résultats, c’est-à-dire un message de statut qui se met à jour souvent.
Sources
- ARIA APG — Alert Pattern — le contrat de comportement d’une alerte
- MDN — Les régions live ARIA — les rôles,
aria-live,aria-atomicet leurs implicites - WCAG — Understanding 4.1.3 Status Messages — pourquoi un message doit être exposé sans prendre le focus
- W3C WAI — Forms Tutorial : User Notifications — les messages de formulaire, annoncés et utiles