Vos animations disparaissent sans transition ? Voici pourquoi
aria-hidden=“true” retire un élément de l’arbre d’accessibilité, pas de l’écran. Si un calque, un menu off-canvas ou une étape d’animation disparaît d’un coup, sans transition, la cause probable est une règle CSS globale du type [aria-hidden="true"] { display: none }, qui confond sémantique et présentation. Le correctif est simple une fois qu’on a compris la confusion.
Ce n’est pas un bug rare. C’est une erreur d’architecture CSS qui se glisse facilement dans un design system, parce qu’elle semble logique à première vue : « si c’est caché aux lecteurs d’écran, autant le cacher visuellement aussi ». Sauf que ce raisonnement inverse le rôle de l’attribut et casse tout composant qui pose aria-hidden="true" avant la fin d’une animation d’entrée ou de sortie.
Pourquoi ça arrive
Plusieurs habitudes de code mènent au même piège :
- Un design system qui cible
[aria-hidden]en CSS pour « simplifier » le masquage, sans distinguer l’intention sémantique de l’intention visuelle. - Des librairies de composants (menus, modales, tooltips, carrousels) qui posent
aria-hidden="true"sur l’arrière-plan avant que l’animation ne soit terminée, et qui ne rangent le focus qu’après coup source : css-tricks, 2026. - Une confusion entre
display,visibilityetaria-hidden, trois mécanismes qui n’agissent ni sur la même couche ni sur la même cible source : Medium, 2025. - Un audit accessibilité fait à l’envers : on corrige les alertes des outils automatisés en ajoutant des règles CSS plutôt qu’en revoyant la structure du composant.
- Un JavaScript qui déplace le focus trop tard, alors que l’élément est déjà sémantiquement masqué mais visuellement encore présent.
1. Diagnostiquer la règle CSS qui masque aria-hidden
Ouvre les DevTools, onglet Styles, et cherche dans les feuilles globales toute occurrence de [aria-hidden], [aria-hidden="true"] ou [aria-hidden=true]. Si tu trouves une règle qui associe cet attribut à display: none, visibility: hidden ou opacity: 0, tu as ta cause.
Vérifie aussi les composants tiers (librairies de modales, sliders, menus) qui injectent leurs propres styles globaux : le bug apparaît souvent après l’ajout d’un composant qui embarque son propre CSS sans isolation.
2. Comprendre ce que fait réellement aria-hidden
aria-hidden est un attribut ARIA qui retire un élément, et tous ses enfants, de l’arbre d’accessibilité exposé aux technologies d’assistance, sans rien changer au rendu visuel source : MDN, 2024. Autrement dit : un lecteur d’écran ignore l’élément, mais il reste visible et occupe sa place dans la mise en page.
C’est l’inverse exact d’une consigne d’affichage CSS. display: none retire l’élément du rendu visuel et le rend automatiquement invisible aussi pour l’accessibilité, puisqu’un élément non rendu est considéré comme caché. aria-hidden fait le chemin inverse : il masque pour l’accessibilité sans toucher au rendu. Coupler les deux dans une seule règle CSS, c’est forcer une équivalence qui n’existe pas dans la spécification.
Un piège supplémentaire : aria-hidden="true" ne doit jamais être posé sur un élément focusable, ni sur un ancêtre d’un élément focusable, car l’attribut est hérité par tous les enfants. Un bouton ou un lien resté accessible au clavier sous un conteneur aria-hidden crée une incohérence que les lecteurs d’écran gèrent différemment selon le navigateur source : W3C WAI, ACT Rule.
3. Séparer sémantique et présentation avec des classes dédiées
La règle à retenir : jamais d’attribut d’accessibilité couplé directement à une propriété d’affichage dans une feuille CSS globale. Deux couches, deux responsabilités.
- Couche sémantique :
aria-hidden,hidden,inert. Ces attributs parlent aux technologies d’assistance et, pourhiddenetinert, au rendu du navigateur. - Couche présentation : des classes CSS pilotées par du JavaScript ou des data-attributes (
.is-hidden,.is-collapsed,data-state="closed"), qui gèrent l’animation, l’opacité, la position.
Un composant qui s’anime doit poser sa classe de transition d’abord, attendre la fin de l’animation (via transitionend ou un délai calé sur la durée CSS), puis seulement ensuite ajuster aria-hidden ou déplacer le focus. C’est exactement l’ordre inversé qui casse Bootstrap, MUI, Shoelace et Angular Material : masquage sémantique posé avant la fin de l’animation, focus rangé après coup source : css-tricks, 2026. Le signalement Bootstrap (issue #41005) documente précisément ce défaut d’ordonnancement.
4. Utiliser inert pour désactiver interaction et visibilité en accessibilité
inert est un attribut HTML global qui rend un élément et tous ses descendants inertes : ils sortent de l’ordre de tabulation, ne peuvent plus recevoir le focus ni être cliqués, et sont retirés de l’arbre d’accessibilité source : MDN, 2026. Contrairement à aria-hidden seul, inert neutralise aussi l’interaction clavier et souris, ce qui règle le piège des menus animés où un lien restait cliquable sous un calque supposé masqué.
Pour une modale ou un panneau off-canvas, inert est le bon outil pour désactiver le reste de la page pendant que le panneau est ouvert. Bootstrap 6 a d’ailleurs contourné le problème autrement : en migrant ses modales vers <dialog> avec showModal(), la boîte de dialogue est placée dans le « top layer » du navigateur, qui rend nativement le reste du document inerte, sans bricolage manuel d’aria-hidden ou d’inert. La PR #41867, qui proposait de remplacer aria-hidden par inert à la main, a d’ailleurs été fermée sans fusion une fois cette bascule faite.
5. Ce qu’on a vu sur un projet réel
Chez Peechy, sur la refonte de la page d’accueil d’un SaaS immobilier en juillet 2026, les calques d’animation marqués aria-hidden pour les lecteurs d’écran disparaissaient purement et simplement à l’écran. La cause : une règle CSS globale du projet contenait [aria-hidden=true] { display: none }, héritée d’un ancien design system. Chaque calque décoratif marqué correctement pour l’accessibilité se retrouvait invisible au chargement, sans transition possible.
La règle qu’on applique depuis sur tous les projets : jamais d’attribut d’accessibilité couplé à une propriété d’affichage dans le CSS global. Si un composant doit être caché visuellement, il porte une classe dédiée. Si un composant doit être caché pour l’accessibilité, il porte aria-hidden, hidden ou inert. Les deux ne se croisent jamais dans une même déclaration CSS.
6. Tester : audit automatisé et navigation clavier réelle
Un audit automatisé (Lighthouse, axe) détecte souvent les cas où un élément reste dans l’ordre de tabulation malgré aria-hidden, mais pas toujours de façon fiable selon le moteur de rendu du navigateur testé. Complète toujours par un test manuel : navigue au clavier (Tab) sur les zones censées être masquées, et vérifie avec un lecteur d’écran (VoiceOver, NVDA) que le contenu masqué n’est jamais annoncé.
Récapitulatif
| Symptôme / cause | Solution |
|---|---|
| Calque disparaît sans transition | Retirer [aria-hidden] { display:none } du CSS global |
| Lien encore cliquable sous un calque masqué | Ajouter inert en plus de aria-hidden |
| Modale qui casse le focus au clavier | Migrer vers <dialog> + showModal() |
| Animation d’entrée/sortie cassée | Poser la classe CSS avant, ajuster aria-hidden après transitionend |
| Confusion sémantique/présentation dans l’équipe | Deux couches distinctes : attributs ARIA/HTML vs classes CSS |
Questions fréquentes
Faut-il retirer aria-hidden des éléments purement décoratifs ?
Non. Le garder est correct : aria-hidden="true" sur un élément décoratif (icône, fond animé sans contenu informatif) évite qu’un lecteur d’écran l’annonce inutilement. Le problème n’est jamais l’attribut lui-même, c’est une règle CSS qui le détourne pour piloter l’affichage.
aria-hidden et display:none, quelle différence concrète ?
display:none retire l’élément du rendu visuel, et il devient automatiquement invisible pour l’accessibilité. aria-hidden="true" fait l’inverse : il masque pour les technologies d’assistance sans toucher au rendu, l’élément reste visible et occupe sa place dans la page.
Quand utiliser inert plutôt que aria-hidden seul ?
Dès qu’un composant doit être à la fois invisible pour l’accessibilité et non interactif (non cliquable, non focusable) : arrière-plan d’une modale, panneau off-canvas fermé, étape de formulaire désactivée. inert gère les deux en un seul attribut natif.
Comment vérifier si mon CSS contient ce bug ?
Cherche [aria-hidden] dans toutes les feuilles de style globales du projet, y compris celles des librairies tierces. Un audit rapide au clavier (Tab sur les zones censées être fermées) révèle aussi les incohérences avant même de lire le code.
Ce bug affecte-t-il le RGAA et la conformité légale ?
Oui, un élément qui disparaît visuellement de façon incohérente ou qui reste focusable alors qu’il est censé être masqué peut échouer sur plusieurs critères RGAA liés à la navigation clavier et à la restitution du contenu. Voir RGAA 5 : les nouveaux critères pour le détail des exigences 2026.
Quand passer la main à un pro
Si le bug touche un design system partagé entre plusieurs pages ou plusieurs sites, mieux vaut le corriger à la racine plutôt que composant par composant. Un audit rapide du CSS global, couplé à un test clavier et lecteur d’écran, suffit en général à localiser toutes les occurrences. Si le projet tourne sur WordPress avec des builders comme Elementor ou WPBakery, consulte notre guide sur l’accessibilité WordPress ou notre retour d’expérience sur l’accessibilité d’un site généré par IA pour les pièges équivalents côté no-code.
Ce problème, Peechy s'en occupe
Plutôt que de tout gérer seul, confiez votre site à une agence qui s'occupe de tout, hébergement, sécurité, maintenance et corrections. Encore plus simple en abonnement : on règle les soucis avant même que vous les remarquiez.
Confier mon site à Peechy