Le chargement et la progression
Un spinner qui tourne ne dit rien à qui ne le voit pas, et la fin d'un chargement est souvent passée sous silence. Trois situations, trois balisages. Démo, code, critères RGAA.
4 critères RGAA en jeu
Essayez-le
1. Une attente dont on ignore la durée
2. Une progression que l'on sait mesurer
3. Un squelette qui occupe la place
| Page | Critères | Conformes |
|---|---|---|
| Accueil | 106 | 91 |
| Recherche | 106 | 84 |
| Contact | 106 | 97 |
Ce qui est réellement passé par les régions d'annonce
Relevé par un observateur du DOM branché sur les trois régions role="status" ci-dessus. Chaque ligne est un texte qu'un
lecteur d'écran a eu à annoncer.
Rien pour l'instant. Lancez l'un des trois traitements.
Lancez les trois traitements et ne quittez pas le clavier : le focus reste sur le bouton que vous venez d’actionner, du début à la fin. Le relevé du bas note ce qui est réellement entré dans les régions d’annonce — il compte deux lignes pour un chargement, trois pour l’export. La dernière de chaque série est la ligne de fin, celle qui manque à l’anti-motif ci-dessous.
Cette démo existe aussi en page autonome.
L’erreur courante
Le spinner purement visuel, posé et retiré par le script.
<!-- À ne pas faire : rien de tout cela n'existe hors de l'écran -->
<svg class="spinner" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" />
</svg> .spinner {
animation: rotation 1s linear infinite;
} Ce svg n’a ni nom, ni texte, et il n’est pas retiré de l’arbre
d’accessibilité : selon le lecteur d’écran il ne s’annonce pas du tout, ou
s’annonce « graphique » sans rien dire de plus. Dans les deux cas, il ne s’est
rien passé : le bouton a été actionné, puis plus rien pendant deux secondes,
puis du contenu est apparu quelque part sans que personne l’annonce.
Et le second temps est pire que le premier. Beaucoup d’interfaces finissent par annoncer le début — « Chargement… » — et beaucoup s’arrêtent là. L’attente devient alors indéfinie : le contenu est là, l’utilisateur ne le sait pas, et il n’a aucun moyen de distinguer un chargement terminé d’un chargement bloqué.
Trois variantes du même oubli :
- La barre dont la valeur n’est que du style.
<div class="barre" style="width: 40%">: les 40 % existent dans la feuille de style et nulle part ailleurs. Rien ne les expose. - Le bouton désactivé pendant le traitement.
bouton.disabled = truesur l’élément qui a le focus : le focus disparaît, revient au corps du document, et il faut retraverser la page pour retrouver sa place. - Le squelette fait de faux mots. Des lignes de « Lorem ipsum » grisées se lisent comme du contenu : le lecteur d’écran énonce un texte de remplissage avec le même sérieux que le vrai.
Le code, pas à pas
1. Une région d’annonce, présente avant le message
<!-- Dans le document dès le départ, vide -->
<p id="etat-commentaires" role="status"></p> etat.textContent = 'Chargement des commentaires…'; role="status" correspond, dans le référentiel, aux messages qui informent
« de la réussite, du résultat d’une action ou bien de l’état d’une
application » — c’est exactement le statut d’un chargement.
Le point qui fait échouer l’annonce tient à l’ordre :
la région doit préexister, vide, et recevoir son texte ensuite. Un conteneur
créé avec son role="status" et son message d’un seul coup n’est pas
systématiquement annoncé — le lecteur d’écran ne surveille que les régions qu’il
connaissait déjà.
role="alert" n’est pas l’équivalent poli : il interrompt la lecture en cours.
Il est réservé aux erreurs et aux suggestions. Un chargement n’est ni l’un ni
l’autre.
2. Annoncer la fin, pas seulement le début
etat.textContent = 'Chargement des commentaires…';
// …
etat.textContent = `${commentaires.length} commentaires chargés.`; Deux messages, toujours. Le premier dit que quelque chose commence, le second que c’est fini et ce qui est arrivé : un nombre, un nom, un résultat. « Terminé » seul oblige à repartir en exploration pour découvrir ce qui a changé.
Le critère 7.5 régit le rôle des messages de statut qui existent, pas leur nombre : il n’exige pas ce second message. C’est une bonne pratique, pas une obligation RGAA — et c’est celle que cette fiche défend.
Si le traitement échoue, le message de fin existe aussi — mais il passe alors
dans une région role="alert", parce que c’est une erreur.
3. La progression mesurable : <progress> d’abord
<label for="export">Export du rapport</label>
<progress id="export" max="100" value="40"></progress> L’élément natif apporte le rôle, la valeur, les bornes et la mise à jour de la
valeur restituée, sans un attribut ARIA. <progress> est un élément
étiquetable : le <label for> lui donne son nom comme à un champ de saisie.
Sans attribut value, la barre est indéterminée : c’est la forme native de
« ça travaille, je ne sais pas combien de temps ».
Si l’apparence impose de la redessiner, le motif ARIA équivalent est explicite :
<div
role="progressbar"
aria-label="Export du rapport"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="40"
></div> Les trois valeurs vont ensemble, et aria-valuenow doit suivre le
mouvement — c’est là que ça casse, l’attribut étant souvent posé au chargement
puis jamais mis à jour. Quand le nombre brut ne parle pas, aria-valuetext donne la formulation utile : « 3 fichiers sur 12 » plutôt que « 25 ».
Ce rôle n’est pas facultatif dès qu’il y a une barre à l’écran. La note
technique du critère 7.5 est explicite : « Dans le cas d’un message de statut
indiquant la progression d’un processus et matérialisé graphiquement par une
barre de progression, un rôle WAI-ARIA progressbar explicite est
nécessaire. »
4. Le rythme des annonces
La barre porte la valeur en continu ; la région d’annonce ne reçoit que des jalons. Un message à chaque pour cent produit cent interruptions et couvre tout le reste de la page.
Le début, la fin, et éventuellement un jalon au milieu suffisent. C’est ce que fait la démo : trois lignes au relevé pour un export complet, pas onze.
Aucun test du critère 7.5 ne fixe cette cadence. Le référentiel demande que les messages de statut qui existent soient restitués, pas qu’ils soient rares : le rythme des annonces relève du jugement d’utilisabilité, hors référentiel.
5. Le squelette : aria-busy, et rien à lire dans le décor
<div id="resultats" aria-busy="true">
<div class="squelette" aria-hidden="true">
<span></span><span></span><span></span>
</div>
</div> aria-busy="true" prévient qu’une zone est en cours de modification et invite
les technologies d’assistance à attendre le résultat plutôt que de restituer un
état intermédiaire. Il se retire quand le contenu est en place.
Les barres grises, elles, n’ont rien à dire : aria-hidden="true". C’est une
précaution qui devient nécessaire dès que le squelette contient du faux texte.
aria-busy ne remplace pas l’annonce : il dit « attendez », pas « c’est
arrivé ». La région role="status" reste indispensable.
aria-busy lui-même ne figure dans aucun test du RGAA : c’est un confort de
restitution, une bonne pratique, pas une obligation. Ce que le référentiel
demande ici, c’est la région d’annonce et un squelette qui ne se lise pas.
6. Le bouton qui lance : ne lui retirez pas le focus
<button type="button" aria-disabled="true">Charger les commentaires</button> if (enCours) return; // le traitement refuse d'être relancé, pas le clavier disabled retire l’élément de l’ordre de tabulation. Appliqué au bouton qui
vient d’être actionné — donc à celui qui a le focus — il détruit le point
d’appui de l’utilisateur au clavier, qui se retrouve au début du document.
aria-disabled="true" annonce l’indisponibilité sans déplacer personne ; c’est
au script de refuser le second déclenchement. Le focus reste où il est, et
l’annonce arrive à quelqu’un qui n’a pas bougé.
7. En avez-vous besoin ?
Sous une seconde, un indicateur visuel n’apporte rien : il apparaît et
disparaît avant d’être lu, et laisse une impression de clignotement. Un simple
message de fin dans la région role="status" suffit alors.
À l’inverse, au-delà de quelques secondes, c’est la progression mesurable qu’il faut chercher à fournir. « 3 documents sur 12 » renseigne ; un voyant qui tourne depuis trente secondes ne dit pas si le traitement avance ou s’il est bloqué.
Ce seuil d’une seconde est un repère de conception, pas une règle du RGAA : aucun critère ne fixe de durée à partir de laquelle un indicateur devient nécessaire, ni en deçà de laquelle il devient superflu.
Au clavier
Un indicateur de chargement n’est pas une commande : il ne prend pas le focus, et il n’a aucun raccourci propre. Ce qui compte, c’est ce que le clavier fait autour de lui.
| Touche | Effet |
|---|---|
| Tab | Ne s’arrête ni sur le voyant, ni sur la barre, ni sur le squelette : aucun n’est actionnable. |
| Entrée / Espace | Lance le traitement depuis le bouton. Le focus reste sur le bouton pendant toute la durée. |
| Flèches en mode navigation (NVDA, JAWS) | Atteignent la barre de progression et relisent sa valeur, là où la tabulation ne s’y arrête pas. |
| Échap | Rien. Un chargement n’est pas une couche à fermer ; si vous offrez l’annulation, c’est un bouton. |
Ce que le lecteur d’écran restitue
Au lancement, la région parle sans que le focus bouge : « Chargement des commentaires… », puis, une fois le traitement fini, « 3 commentaires chargés. ». Sur la barre atteinte en mode navigation : « Export du rapport, barre de progression, 40 % ». La formulation varie d’un lecteur à l’autre, pas les informations : un nom, la nature de l’élément, la valeur.
Le diagnostic tient en trois constats. Si vous n’entendez rien au lancement, la région a probablement été créée en même temps que le message. Si vous entendez le début et jamais la fin, il manque le second message — le défaut que cette fiche vise. Si vous entendez « Export du rapport, 40 » sans « barre de progression », le rôle manque et la valeur flotte sans son unité.
Checklist
- Une région
role="status"présente dans le document avant le message. <progress>étiqueté, ourole="progressbar"avec ses trois valeurs tenues à jour.- Le voyant et le squelette retirés de l’arbre d’accessibilité.
aria-disabledsur le bouton qui lance, jamaisdisabled: le focus doit rester.
Au-delà du référentiel — des bonnes pratiques, pas des exigences RGAA :
- Deux annonces par traitement : le début, et la fin avec son résultat.
- Des jalons, pas un message par pour cent.
- Sous une seconde, pas d’indicateur — seulement le message de fin.
Les critères RGAA en jeu
- Critère 7.5 — Messages de statut
« Chargement… » et « 6 résultats chargés » sont des messages de statut : sans région d'annonce, ils n'existent que pour qui regarde l'endroit où ils s'affichent. Et le référentiel exige un rôle progressbar explicite dès qu'une barre de progression matérialise l'attente.
- Critère 7.3 — Contrôle des scripts au clavier
Désactiver le bouton qui vient d'être actionné pendant que le traitement tourne supprime le focus de l'élément qui le portait : il retombe sur le corps du document, et l'utilisateur au clavier repart du haut de la page.
- Critère 1.2 — Images décoratives ignorées
Le voyant qui tourne, dessiné en svg, est une image de décoration : sans aria-hidden, il s'annonce à l'endroit même où l'utilisateur attend une information précise, et double ce que le texte dit déjà.
- Critère 7.2 — Pertinence de l’alternative au script
Une jauge dessinée en image plutôt que déclarée en barre de progression garde souvent l'alternative qu'elle avait au départ : « 0 % » décrit alors un état qui n'existe plus.
Vérifiez votre composant avec l'extension
Pour aller plus loin
- Critère 7.5 — Messages de statut : la classification complète
entre
status,alertetprogressbar, et ce qu’un audit y vérifie. - « Message de statut » au glossaire : la définition qui décide de ce qui relève du 7.5, et de ce qui n’en relève pas.
- Le récapitulatif d’erreurs : l’autre
usage des régions d’annonce, avec cette fois
role="alert"et du focus à conduire. - Le téléversement de fichier : le composant qui combine les deux, une confirmation et un refus.
Sources
- MDN —
<progress>— l’élément natif, ses bornes et son état indéterminé - MDN — ARIA
progressbarrole — le motif à reproduire quand l’élément natif ne convient pas - MDN — Régions live ARIA — pourquoi la région doit préexister au message
- WCAG — Understanding 4.1.3 Status Messages — le critère WCAG dont le 7.5 est la transposition