RGAA Lab

Composants accessibles

Le carrousel

Le carrousel est le composant qu'on installe le plus souvent sans besoin, et qui coûte le plus cher à rendre accessible. Si vous en faites un : le défilement doit pouvoir s'arrêter, et les diapositives masquées doivent l'être vraiment.

4 critères RGAA en jeu

Essayez-le

Diapositive 1 sur 4

L'accordéon

Plier du contenu sans inventer un seul rôle ARIA : un bouton, un état, un panneau vraiment masqué.

Lire la fiche accordéon

Il démarre en défilant — sauf si votre système demande des animations réduites. Arrêtez-le, puis parcourez-le à la tabulation : quatre arrêts seulement, les trois commandes et le lien de la diapositive affichée. Les trois autres liens existent dans le DOM et ne sont atteignables par personne, ce que le bouton d’inspection vérifie sur le composant lui-même.

Cette démo existe aussi en page autonome.

La question à poser d’abord

Un carrousel est presque toujours un désaccord d’organisation transformé en composant : quatre services veulent la même place en haut de page, et le carrousel leur permet de ne pas trancher. Les mesures publiées par les sites qui en ont retiré un vont dans le même sens — la première diapositive concentre l’essentiel des clics, les suivantes n’en reçoivent presque aucun.

Le coût, lui, est bien réel : du mouvement à contrôler, des commandes à nommer, un masquage à faire correctement, une position à annoncer. Quatre problèmes qu’un empilement de trois blocs, ou un seul message bien choisi, ne pose pas.

Si la décision est prise malgré tout, la suite dit comment le faire tenir.

L’erreur courante

Le carrousel de bibliothèque, posé tel quel : les diapositives sont décalées en CSS, et il tourne tout seul.

<!-- À ne pas faire -->
<div class="carrousel">
	<div class="piste" style="transform: translateX(-100%)">
		<div class="diapo"><a href="/offre-1">En savoir plus</a></div>
		<div class="diapo"><a href="/offre-2">En savoir plus</a></div>
		<div class="diapo"><a href="/offre-3">En savoir plus</a></div>
	</div>
	<button class="fleche precedent"></button>
	<button class="fleche suivant"></button>
</div>
/* À ne pas faire : les diapositives sont hors du cadre, pas hors de la page */
.carrousel {
	overflow: hidden;
}

Trois fautes, et aucune ne se voit à la souris.

Le défilement automatique n’a aucun moyen d’arrêt. Le texte d’une diapositive s’en va avant qu’on l’ait fini — ce qui gêne une lecture lente, une dyslexie, un trouble de l’attention, et suffit à faire échouer le critère 13.8.

Les diapositives hors du cadre sont toujours là. overflow: hidden coupe l’affichage, pas l’ordre de tabulation : les trois liens « En savoir plus » restent focalisables, et le focus disparaît de l’écran pendant deux tabulations avant de reparaître.

Les flèches n’ont pas de nom. Un <button> dont le contenu est une icône CSS est annoncé « bouton », sans plus, et rien ne distingue le précédent du suivant.

Trois variantes, tout aussi répandues :

  • La pause au survol seule. Le glossaire du référentiel est explicite : un arrêt déclenché uniquement à la prise de focus ne valide pas le critère — et le survol ne concerne ni le clavier ni le doigt.
  • Les pastilles pour seule position. Un rond plein parmi trois ronds vides ne dit rien à qui ne voit pas, et rien non plus à qui ne distingue pas la nuance.
  • Le lien sur toute la diapositive, dupliqué avec le lien du bouton : deux entrées dans la liste des liens pour une seule destination.

Le code, pas à pas

1. L’arrêt, avant tout le reste

C’est le point qu’on rate le plus souvent, et il a un texte précis. Le critère 13.8 demande que chaque contenu en mouvement déclenché automatiquement vérifie une de ces conditions : la durée du mouvement est inférieure ou égale à 5 secondes, l’utilisateur peut l’arrêter et le relancer, il peut afficher et masquer le contenu en mouvement, ou il peut afficher la totalité de l’information sans le mouvement.

Un carrousel qui tourne en boucle ne peut pas se rabattre sur la première condition. Restent trois voies : arrêter et relancer, masquer le contenu en mouvement, ou donner accès à la totalité de l’information sans mouvement. Les deux dernières valident le critère, mais au prix du composant — masquer le carrousel ou déplier ses quatre diapositives, c’est reconnaître qu’un empilement de blocs suffisait. La première est la seule qui garde le carrousel utilisable ; c’est celle qu’on montre ici :

<button type="button" id="arret">Arrêter le défilement</button>
arret.addEventListener('click', () => {
	enMarche = !enMarche;
	arret.textContent = enMarche ? 'Arrêter le défilement' : 'Relancer le défilement';
	// …démarrer ou couper la minuterie
});

L’intitulé change avec l’état : c’est plus simple et plus sûr qu’un aria-pressed sur un bouton dont le libellé resterait figé.

La définition du contrôle d’un contenu en mouvement ajoute deux notes, qu’on cite mot pour mot parce que les définitions du glossaire sont limitatives :

Note 1 : lorsque c’est approprié, la méthode de contrôle devrait être disponible comme premier élément de la page.

Note 2 : la méthode de contrôle du contenu en mouvement ou clignotant doit permettre à l’utilisateur d’interagir avec le reste de la page. En conséquence, l’arrêt ou la mise en pause via un événement déclenché uniquement sur la prise de focus ne permet pas de valider le critère.

La Note 1 vise la page, pas le composant, et elle emploie devrait : c’est une recommandation, qui prend tout son sens pour un contenu en mouvement présent dès le chargement. Pour un carrousel en milieu de page, on retient ce qu’elle protège plutôt que sa lettre — que le contrôle soit atteignable avant ce qui bouge, donc placé en tête du composant. La Note 2, elle, est une exclusion ferme : un arrêt qui ne se déclenche qu’à la prise de focus ne compte pas.

Enfin, prefers-reduced-motion mérite d’être respecté, mais ne remplace rien : c’est une bonne pratique, quand le référentiel demande un contrôle dans la page.

2. Une seule diapositive dans le flux

<div class="scene">
	<div class="diapositive"></div>
	<div class="diapositive" hidden></div>
	<div class="diapositive" hidden></div>
</div>

hidden retire la diapositive de l’affichage et de l’ordre de tabulation. C’est ce qui règle le piège du focus perdu, et c’est exactement ce que le critère 10.8 attend d’un contenu caché : qu’il ait vocation à être ignoré tant qu’aucune action ne le révèle.

Si vous tenez à l’animation de glissement, hidden ne s’y prête pas seul. La solution honnête est de rendre les diapositives sortantes inertes plutôt que de renoncer à les masquer : inert sur les diapositives non affichées les retire du focus et de l’arbre d’accessibilité tout en les laissant à l’écran le temps de la transition. Ce qu’il ne faut pas faire, c’est abandonner le masquage pour pouvoir animer.

3. Des boutons, avec un nom

<button type="button"><span aria-hidden="true"></span> Diapositive précédente</button>
<button type="button">Diapositive suivante <span aria-hidden="true"></span></button>

Le nom est du texte visible, et le chevron est masqué parce qu’il n’ajoute rien à l’oreille. C’est le montage le plus robuste : pas d’aria-label à maintenir, et l’intitulé visible se retrouve tel quel dans le nom accessible — ce que le test 7.1.3 demande.

Le carrousel lui-même gagne à être une région nommée :

<section aria-label="Les composants de cette collection"></section>

Sans ce nom, la <section> n’est pas une région du tout : elle ne prend le rôle region que si elle porte un nom accessible, et n’apparaît donc dans aucune liste de régions. Le nom est ce qui la fait exister comme point de repère.

4. La position, annoncée — mais pas toutes les quatre secondes

<div class="scene" aria-live="off">
	<p>Diapositive 1 sur 4</p></div>
scene.setAttribute('aria-live', enMarche ? 'off' : 'polite');

La position s’écrit en toutes lettres, pas en pastilles : « Diapositive 2 sur 4 » se lit à l’œil comme à l’oreille, et les ronds ne sont plus qu’un doublon décoratif, qu’on masque par aria-hidden.

Le basculement d’aria-live est le point qu’on rate. Une zone polite pendant le défilement automatique fait annoncer une diapositive toutes les quatre secondes : la page devient inutilisable, et l’annonce couvre tout ce que l’utilisateur essayait de lire ailleurs. Muette tant que ça tourne, polie une fois arrêté : c’est le seul réglage tenable.

La zone doit exister dès le premier rendu, même vide. Créée au moment de l’annonce, elle n’est pas restituée.

Au clavier

ToucheEffet
TabAtteint l’arrêt du défilement, puis les deux flèches, puis le contenu de la seule diapositive affichée.
Entrée / EspaceActionne le bouton qui a le focus ; le focus reste dessus, la diapositive change sous lui.
Rien, et c’est un choix : un carrousel n’est pas un groupe où l’on choisit, mais une suite qu’on fait avancer.

Les flèches ne deviennent attendues que si vous ajoutez un sélecteur de diapositive construit comme des onglets — auquel cas c’est le motif des onglets qui s’applique, tabindex roulant compris.

Un dernier point de clavier, qui ne se voit pas dans un tableau : le défilement ne doit pas emporter la diapositive sous les doigts de quelqu’un qui vient d’y poser le focus. Suspendre la minuterie tant que le focus est dans le carrousel est un complément utile — un complément, pas le moyen de contrôle que le référentiel exige.

Ce que le lecteur d’écran restitue

À l’entrée dans la région : son nom et sa nature — « Les composants de cette collection, région ». Sur la première commande : « Arrêter le défilement, bouton », puis « Relancer le défilement, bouton » après activation. Sur la zone des diapositives, une fois le défilement arrêté : « Diapositive 2 sur 4 », suivi du contenu qui vient d’apparaître. La formulation change d’un lecteur à l’autre, les informations non.

Si vous n’entendez que « bouton, bouton » sur les flèches, elles n’ont pas de nom. Si vous entendez des liens qui ne correspondent à rien de visible, les diapositives masquées sont encore dans le flux. Et si une diapositive s’annonce toute seule pendant que vous lisez ailleurs, la zone live est restée polite alors que le carrousel tourne.

Checklist

  • Une des conditions du 13.8 vérifiée ; si vous gardez le carrousel, c’est un moyen d’arrêter et de relancer le défilement, dans la page, atteignable au clavier.
  • Ce moyen placé avant le contenu qui bouge, et pas seulement au survol.
  • Les diapositives non affichées masquées par hidden — ou rendues inert si vous animez.
  • Des boutons avec un intitulé visible : « diapositive précédente », « suivante ».
  • La position écrite en toutes lettres, les pastilles masquées par aria-hidden.
  • aria-live="off" tant que ça défile, polite une fois arrêté.
  • Avant tout cela : vérifiez qu’un empilement de blocs ne ferait pas mieux.

Les critères RGAA en jeu

  • Critère 13.8 — Contrôle des contenus en mouvement

    Un carrousel qui défile seul plus de cinq secondes doit vérifier une des conditions du 13.8 — en pratique, offrir un moyen de l'arrêter et de le relancer dans la page. C'est ce qui manque le plus souvent.

  • Critère 7.1 — Compatibilité des scripts

    Les commandes du carrousel doivent porter un nom et la position dans la série doit être exposée : une flèche sans intitulé et une pastille colorée ne disent rien à une technologie d'assistance.

  • Critère 7.3 — Contrôle des scripts au clavier

    Changer de diapositive et arrêter le défilement doivent se faire au clavier ; un carrousel qui ne s'arrête qu'au survol de la souris ne s'arrête pour personne d'autre.

  • Critère 12.8 — Cohérence de l’ordre de tabulation

    Une diapositive masquée doit l'être pour tout le monde — hidden ou inert, pas un simple décalage, qui laisse ses liens dans l'ordre de tabulation et envoie le focus se promener dans un contenu qu'on ne voit pas.

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources