Carrousel accessible : bouton pause, critère 13.8 et ARIA
Rendre un carrousel conforme RGAA : bouton pause du défilement automatique (critère 13.8), pause au survol et au focus, diapositives masquées avec hidden, aria-roledescription.
Le carrousel est une région qui présente plusieurs contenus en séquence au même emplacement, généralement une diapositive à la fois, avec des contrôles précédent et suivant et, parfois, un défilement automatique. Il n'existe aucun élément HTML natif. La façon la plus simple et la plus sûre de rendre un carrousel conforme tient en une phrase : ne le faites pas défiler automatiquement. La rotation automatique apporte peu et coûte cher : un bouton pause visible, la mise en pause au survol ET au focus, la gestion d'une zone aria-live et un chemin prefers-reduced-motion. Sans elle, le critère 13.8 est satisfait par construction et il ne reste presque rien à casser.
Quand ce motif est le mauvais choix
Le carrousel se justifie quand plusieurs blocs de contenu doivent réellement partager le même emplacement à l'écran. Si l'objectif est « faire tenir cinq messages sur la page d'accueil », une liste statique de cartes est presque toujours plus efficace et plus accessible : les taux de clic des carrousels sont notoirement faibles, et la quasi-totalité des clics vont à la première diapositive.
Si le carrousel est retenu malgré tout :
- préférez la variante sans défilement automatique, contrôlée uniquement par les boutons précédent et suivant ;
- la variante à pastilles de sélection en
role="tab"n'est valide que si les pastilles se comportent réellement comme des onglets (navigation aux flèches, panneau associé) ; des pastilles décoratives n'ont pas à porter ce rôle.
Pour le débat complet sur l'opportunité même du composant et ses alternatives : Carrousels accessibles : faut-il vraiment les utiliser ?
Critères RGAA applicables
- Critère 13.8 : contenu en mouvement ou clignotant contrôlable. LE critère du motif. Le test 13.8.1 n'exempte qu'un mouvement déclenché automatiquement dont la durée est inférieure ou égale à 5 secondes : un défilement automatique de plus de 5 secondes doit pouvoir être arrêté et relancé par l'utilisateur. Un carrousel qui défile sans contrôle de pause est une non-conformité directe.
- Critère 7.1 : compatibilité des scripts avec les technologies d'assistance. Les contrôles et l'état de défilement doivent être exposés ;
aria-roledescription="carrousel"renomme la région pour les technologies d'assistance. - Critère 7.3 : script contrôlable au clavier et par tout dispositif de pointage. Les boutons précédent, suivant et pause doivent être atteignables et activables au clavier.
- Critère 7.5 : messages de statut. Le changement de diapositive est un message de statut ; une zone
aria-live="polite"sur le conteneur des diapositives l'annonce, mais UNIQUEMENT quand le défilement automatique est arrêté. - Critère 10.7 : prise de focus visible. Les contrôles sont souvent posés sur des visuels chargés : prévoir un indicateur de focus qui contraste sur n'importe quel fond, un double liseré par exemple.
- Critère 10.8 : contenus cachés ignorés des technologies d'assistance. Les diapositives hors écran doivent être masquées avec
hiddenouinert; une simple translation CSS les laisse dans l'arbre d'accessibilité et dans l'ordre de tabulation.
Interaction clavier attendue
| Touche | Action |
|---|---|
| Tab / Maj + Tab | Parcourt les éléments interactifs : le bouton pause d'abord (premier dans le DOM), puis les contenus de la diapositive visible, puis précédent et suivant. |
| Entrée / Espace (bouton pause) | Arrête ou relance le défilement automatique. |
| Entrée / Espace (précédent / suivant) | Affiche la diapositive précédente ou suivante. |
Règle comportementale en plus des touches : l'arrivée du focus dans le carrousel doit suspendre le défilement automatique. Sans cela, l'utilisateur clavier tabule vers un lien de la diapositive 2 et le carrousel avance, déplaçant le lien sous ses doigts.
Rôles et attributs ARIA
Le conteneur est une région nommée : aria-roledescription="carrousel" en change l'annonce mais ne constitue PAS un nom, un aria-label reste obligatoire. Chaque diapositive porte role="group", aria-roledescription="diapositive" et un nom donnant la position (« 1 sur 3 »). Si le carrousel défile automatiquement, le bouton de pause est visible et placé AVANT les diapositives dans le DOM, pour que l'utilisateur puisse arrêter le mouvement avant de tabuler dedans.
<section id="carrousel" aria-roledescription="carrousel" aria-label="Actualités">
<!-- Le bouton pause vient EN PREMIER dans le DOM (critère 13.8). -->
<button type="button" id="carrousel-pause">
<svg aria-hidden="true" focusable="false"><use href="#icone-pause"/></svg>
<span class="sr-only">Mettre en pause le défilement automatique</span>
</button>
<!-- aria-live est géré en JS : "off" pendant la rotation, "polite" à l'arrêt. -->
<div id="carrousel-diapos" aria-live="off">
<div role="group" aria-roledescription="diapositive" aria-label="1 sur 3">
<img src="actu-1.jpg" alt="">
<h3><a href="/actualites/1">Publication du schéma pluriannuel</a></h3>
</div>
<div role="group" aria-roledescription="diapositive" aria-label="2 sur 3" hidden>…</div>
<div role="group" aria-roledescription="diapositive" aria-label="3 sur 3" hidden>…</div>
</div>
<button type="button" id="carrousel-prec"><span class="sr-only">Diapositive précédente</span></button>
<button type="button" id="carrousel-suiv"><span class="sr-only">Diapositive suivante</span></button>
</section>
/* Les contrôles se posent sur des photos : un liseré d'une seule couleur peut
disparaître sur une image claire. Le double liseré garantit le contraste
sur n'importe quel fond (critère 10.7). */
#carrousel button:focus-visible {
outline: 3px solid #fff;
outline-offset: 2px;
box-shadow: 0 0 0 5px #000;
}
const carrousel = document.getElementById('carrousel');
const conteneur = document.getElementById('carrousel-diapos');
const diapos = [...carrousel.querySelectorAll('[role="group"]')];
const pauseBtn = document.getElementById('carrousel-pause');
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
let index = 0;
let timer = null;
function afficher(i) {
index = (i + diapos.length) % diapos.length;
// `hidden`, pas une translation : les diapositives hors écran doivent sortir
// de l'arbre d'accessibilité ET de l'ordre de tabulation (critère 10.8).
diapos.forEach((diapo, n) => { diapo.hidden = n !== index; });
}
function lancer() {
if (reduceMotion.matches) return; // jamais de rotation sous reduced-motion
timer = setInterval(() => afficher(index + 1), 5000);
// En rotation : annoncer chaque diapositive serait incessant.
conteneur.setAttribute('aria-live', 'off');
pauseBtn.querySelector('.sr-only').textContent = 'Mettre en pause le défilement automatique';
}
function arreter() {
clearInterval(timer);
timer = null;
// À l'arrêt, les changements sont voulus par l'utilisateur : les annoncer est utile.
conteneur.setAttribute('aria-live', 'polite');
pauseBtn.querySelector('.sr-only').textContent = 'Relancer le défilement automatique';
}
pauseBtn.addEventListener('click', () => (timer ? arreter() : lancer()));
// Une action explicite (précédent / suivant) arrête la rotation pour la session.
document.getElementById('carrousel-suiv').addEventListener('click', () => { arreter(); afficher(index + 1); });
document.getElementById('carrousel-prec').addEventListener('click', () => { arreter(); afficher(index - 1); });
// Pause au survol ET au focus : un utilisateur clavier ne survole jamais.
carrousel.addEventListener('mouseenter', () => timer && arreter());
carrousel.addEventListener('focusin', () => timer && arreter());
À ne pas faire :
<!-- Rotation automatique sans contrôle de pause : non-conformité directe
au critère 13.8, pas un simple avertissement. -->
<div class="carrousel" data-autoplay="5000">…</div>
<!-- aria-live="polite" sur un conteneur en rotation : une diapositive est
annoncée toutes les 5 secondes, indéfiniment, par-dessus ce que
l'utilisateur lisait ailleurs. -->
<div class="carrousel-diapos" aria-live="polite">…</div>
<!-- aria-roledescription sans nom accessible : annoncé « carrousel » sans
dire lequel. roledescription RENOMME le rôle, il ne nomme pas la région. -->
<section aria-roledescription="carrousel">…</section>
/* À ne pas faire : masquer les diapositives par translation. Les trois
diapositives restent dans l'arbre d'accessibilité et dans l'ordre de
tabulation : l'utilisateur tabule dans des liens invisibles (critère 10.8). */
.diapo { transform: translateX(100%); }
.diapo--active { transform: translateX(0); }
Sous prefers-reduced-motion: reduce, ne pas défiler automatiquement du tout, pas seulement ralentir. Et une action explicite de l'utilisateur (clic sur suivant) doit arrêter la rotation pour la session, jamais la relancer.
Défauts fréquents et impact utilisateur
- Défilement automatique sans bouton de pause : non-conformité directe au critère 13.8. Toute personne qui lit lentement, ou dont le lecteur d'écran est au milieu d'une phrase, perd le contenu.
- Pause au survol seulement, pas au focus : un utilisateur clavier ne survole jamais ; il tabule vers un lien de la diapositive 2 et le carrousel avance, déplaçant le lien sous ses doigts.
- Diapositives masquées par translation CSS : tous les liens de toutes les diapositives restent tabulables en même temps ; l'utilisateur tabule dans du contenu invisible (critère 10.8). C'est le comportement par défaut de la plupart des bibliothèques de carrousel : vérifiez avant d'en adopter une.
aria-live="polite"actif pendant la rotation : une nouvelle diapositive est annoncée en boucle, en interrompant ce que l'utilisateur lisait ailleurs sur la page.- Contrôles en icônes sans nom accessible : annoncés « bouton, bouton, bouton » ; pastilles sans libellé qui n'indiquent ni la position ni la destination.
- Rotation maintenue malgré
prefers-reduced-motion: reduce: pour une personne avec un trouble vestibulaire, une transition non sollicitée toutes les quelques secondes peut provoquer de réelles nausées.
Ce que les outils automatiques ne détectent pas
Un scanner automatique, le nôtre compris, repère les boutons en icône sans nom accessible et certains attributs ARIA incohérents. Mais l'essentiel du critère 13.8 lui échappe : la présence d'un défilement automatique, l'absence de bouton de pause, la pause au survol sans pause au focus. Restent également manuels :
- les diapositives masquées par translation : seul un parcours à Tab révèle qu'on atteint des liens invisibles ;
- le comportement sous
prefers-reduced-motion: reduce(aucune rotation attendue) ; - le silence du lecteur d'écran pendant la rotation, et l'annonce des changements une fois la rotation arrêtée ;
- la rotation qui se suspend réellement quand le focus entre dans le carrousel.
Ce motif se vérifie presque entièrement à la main.
Vérifier ce composant
Protocole manuel, dans cet ordre :
- Le test du critère 13.8 d'abord : le carrousel défile-t-il automatiquement ? Si oui, existe-t-il un contrôle de pause visible, atteignable au clavier, qui l'arrête réellement ? Un non est une non-conformité, pas un avertissement.
- Le test du contenu qui se dérobe : lancez la rotation, puis tabulez vers un lien de la diapositive visible. La rotation doit se suspendre. Si la diapositive change pendant que vous tabulez, le lien se déplace sous vos doigts.
- Le test de l'ordre de tabulation : traversez tout le carrousel à Tab. Vous ne devez atteindre que les liens de la diapositive visible. Si vous passez sur des liens de diapositives invisibles, elles sont masquées par translation (critère 10.8).
- Reduced motion : activez
prefers-reduced-motion: reduce. Le carrousel ne doit plus tourner du tout. - Au lecteur d'écran : silence pendant la rotation ; annonce des changements de diapositive une fois la rotation arrêtée. Une annonce spontanée toutes les 5 secondes signale un
aria-liveactif pendant la rotation.
Deux outils gratuits du site pour appuyer la vérification : le testeur de focus visible valide vos styles :focus sur des contrôles posés sur des visuels chargés (critère 10.7), et le simulateur de lecteur d'écran affiche l'arbre d'accessibilité : vous y verrez immédiatement si les diapositives masquées en sortent réellement et si chaque bouton a un nom.
Approfondir avec les fiches critères
Vérifier ce composant sur votre site ?
Le scan repère les défauts détectables automatiquement (rôles incohérents, champs sans étiquette, attributs ARIA orphelins) ; le reste se vérifie à la main avec les protocoles de cette fiche.
Lancer un scan gratuit