Le symptôme en production : des tickets de support qui ressemblent à « votre site me donne la nausée », « le défilement ressemble à un tremblement de terre » ou « la page de connexion se rebelle contre moi ». Ce ne sont pas des « préférences de design ». Ce sont des bugs de fiabilité pour des humains.
Si vous déployez des animations sans une vraie stratégie pour le mouvement réduit, vous jouez aux dés avec l’accessibilité, la conversion et la réponse aux incidents. La bonne nouvelle : prefers-reduced-motion est l’une des rares fonctionnalités de la plateforme web à la fois humaine et peu coûteuse opérationnellement — à condition de l’implémenter avec rigueur.
Ce que prefers-reduced-motion est vraiment (et ce que ce n’est pas)
prefers-reduced-motion est une préférence utilisateur exposée à la plateforme web via des media queries CSS et des API JavaScript. C’est un signal indiquant que l’utilisateur souhaite moins de mouvement. Moins de mouvement peut signifier moins de transitions, pas de parallaxe, pas d’animations en lecture automatique, pas de « scroll-jacking » et pas de défilement « fluide utile » qui traîne la page sous leur curseur comme si elle était remorquée.
Ce que ce n’est pas :
- Une demande pour rendre votre UI moche.
- Une suggestion de réduire le taux de rafraîchissement tout en gardant la même animation (ce qui peut être pire).
- Un endroit pour déverser tous les « on ne veut pas déboguer ça » fallback. Le mouvement réduit n’est pas une « réduction de qualité ».
Considérez le mouvement comme une dépendance de production. Il peut échouer. Il peut surcharger un appareil client. Et il peut nuire aux utilisateurs. Supporter le mouvement réduit est votre disjoncteur.
La réalité opérationnelle : les bugs de mouvement sont des bugs de systèmes distribués
Les animations ne fonctionnent pas dans un isolat. Elles interagissent avec :
- la planification CPU/GPU (surtout sur les graphiques intégrés et en modes basse consommation)
- les périphériques d’entrée (trackpads, souris à molette, tactile)
- la performance de layout et de peinture (que vous pouvez absolument saboter vous-même)
- le cycle de vie du framework (rerenders React/Angular/Vue vs. état d’animation)
- les widgets tiers (publicités, chat, analytics, A/B testing)
Quand le mouvement réduit est activé, votre système fonctionne effectivement en un mode runtime différent. Traitez-le comme tel : testez-le, surveillez-le et empêchez-le de dériver.
Une citation à laquelle je reviens souvent, parce qu’elle s’applique directement ici : « L’espoir n’est pas une stratégie. »
— Gene Kranz
(Votre stratégie d’animation ne devrait pas être « espérer que les utilisateurs ne remarquent pas ». Ils le remarquent.)
Contexte historique et faits intéressants
- Les réglages OS de « réduire le mouvement » préexistaient à la fonction web. Les plateformes ont ajouté la réduction du mouvement principalement pour l’accessibilité et le confort vestibulaire ; le web a ensuite obtenu un crochet standardisé.
- prefers-reduced-motion est une fonctionnalité de Media Queries Level 5. Elle fait partie de la vague moderne de signaux de « préférences utilisateur » aux côtés du schéma clair/obscur.
- Le signal est généralement binaire-ish, mais l’implémentation varie. On voit habituellement
reducevsno-preference; certains environnements peuvent se comporter différemment en contextes embarqués. - Le « smooth scrolling » est devenu un défaut grand public étonnamment vite. Les designers l’ont adoré ; certains utilisateurs se sont sentis physiquement mal. La solution au niveau spec n’a pas été « l’interdire » mais « respecter la préférence ».
- Les troubles vestibulaires ne sont pas des cas marginaux rares. La sensibilité au mouvement peut venir de migraines, problèmes d’oreille interne, commotions, médicaments, ou simplement du vieillissement. Votre base d’utilisateurs n’est pas un échantillon de passionnés d’effets graphiques de 25 ans.
- La parallaxe est souvent fautive. Elle couple l’entrée de scroll à un mouvement en couches ; pour certains utilisateurs, c’est comme si le monde glissait sous eux.
- Les animations en lecture automatique sont souvent pires que les transitions. Elles peuvent être implacables, surtout lorsqu’elles bouclent dans la vision périphérique.
- Performance et accessibilité sont ici intriquées. Une animation saccadée (frames perdues) peut déclencher de l’inconfort davantage qu’un mouvement fluide, donc « laissez-la juste ramer » n’est pas une solution.
- Le mouvement réduit peut améliorer les métriques business. Pas parce que le mouvement est « mauvais », mais parce que supprimer distractions et nausées réduit souvent le taux de rebond et les clics rageurs.
Blague #1 : Les animations sont comme des stagiaires — super sous supervision, catastrophiques quand on les laisse « s’exprimer » en production.
Définition de terminé : mouvement réduit qui ne régresse pas
La plupart des équipes « supportent le mouvement réduit » en saupoudrant quelques règles CSS dans un coin puis en considérant le travail terminé. C’est comme ajouter une règle de firewall et déclarer l’entreprise sécurisée.
Voici une définition pratique de terminé qui survit aux refontes et migrations de framework :
1) Vous avez une source unique de vérité pour la préférence
Le CSS peut la lire via des media queries, le JS via matchMedia. Mais votre application ne devrait pas avoir cinq utilitaires « isReducedMotion » différents qui se contredisent.
2) Vous avez classifié le mouvement, pas seulement éteint tout
Tous les mouvements ne se valent pas. Catégorisez :
- Essentiel : communique un changement d’état (par ex., anneau de focus, changements subtils d’opacité) et peut être raccourci plutôt qu’enlevé.
- Utile : améliore la compréhension (par ex., une courte transition d’expansion/réduction). En mode réduit, raccourcir, supprimer l’overshoot, supprimer le rebond.
- Décoratif : boucles en arrière-plan, confettis, parallaxe, cartes « qui respirent ». En mode réduit, désactiver.
3) Le mouvement réduit fonctionne sans JS
Commencez par le CSS, puis affinez avec du JS. Si le JS échoue, le mouvement réduit doit toujours être respecté pour la majorité des effets.
4) Les réglages de mouvement sont testés comme une variante de première classe
Pas « quelqu’un l’a vérifié une fois sur un MacBook ». Vous voulez :
- couverture de regression visuelle pour le mode réduit
- tests unitaires autour de vos utilitaires de motion
- une étape QA explicite dans la validation de sortie
5) Les sources d’animation tierces sont contrôlées
Lottie, embeds marketing, widgets de chat, overlays analytics — ce sont les coupables habituels. Vous avez besoin d’une politique : soit ils respectent le mouvement réduit, soit ils ne sont pas déployés.
6) Vous pouvez expliquer ce qui se passe lors d’un basculement
Les utilisateurs peuvent changer le réglage OS pendant que votre application est ouverte. Votre code doit y répondre. Si vos animations ne vérifient que lors du démarrage, vous avez construit une fonctionnalité « qui marche en démonstration ».
Guide de diagnostic rapide
Vous êtes d’astreinte (ou vous faites semblant de ne pas l’être). Un ticket arrive : « Le mouvement réduit est activé, mais le site manifeste toujours des animations. » Ou pire : « Le mouvement réduit est activé et le site est maintenant cassé. » Voici comment trouver rapidement le goulot.
Première étape : confirmez que la préférence est bien « reduce » dans l’environnement
- Le réglage OS est-il activé ?
- Le navigateur l’expose-t-il (pas dans un webview embarqué bizarre) ?
- Votre code le lit-il correctement ?
Deuxième étape : identifiez d’où provient le mouvement
- transitions/animations CSS
- animations pilotées par JS (boucles requestAnimationFrame)
- comportement de défilement (smooth scrolling CSS ou librairies JS)
- boucles canvas/webgl
- iframes embarqués ou scripts tiers
Troisième étape : vérifiez si « reduce » est implémenté comme « désactiver » ou « raccourcir »
Si vous fixez des durées à 0ms globalement, vous pouvez créer de nouveaux bugs : sauts de focus, thrashing de layout, problèmes de timing d’événements, listeners « animationend » qui ne se déclenchent jamais, etc.
Quatrième étape : vérifiez les mises à jour runtime
Basculez le réglage OS pendant que l’app est ouverte. Si rien ne change, vous avez probablement une valeur figée au chargement du module.
Cinquième étape : cherchez les régressions introduites par des optimisations
Coupables courants : « améliorations de perf » qui ont déplacé la logique d’animation dans un utilitaire partagé qui ne lit plus la préférence, ou des refactors CSS qui ont remplacé des propriétés sécuritaires par des propriétés déclenchant le layout.
Patrons d’implémentation : CSS, JS et systèmes de composants
CSS : la base dont vous devez toujours disposer
Commencez par une politique qui s’applique à toute l’app. Une base typique :
- désactiver les animations décoratives longues
- réduire ou supprimer les transitions (surtout les mouvements « fly in » basés sur transform)
- désactiver le défilement fluide
Le mécanisme CSS est simple :
@media (prefers-reduced-motion: reduce)pour le mode réduit@media (prefers-reduced-motion: no-preference)pour le mode par défaut
Mais « simple » est l’endroit où les bugs naissent. Vous devez savoir quoi désactiver, et où des règles globales se retournent contre vous :
Le piège du reset global
Vous avez déjà vu ce snippet :
- set
animation-duration: 0.001ms !important - set
transition-duration: 0.001ms !important
C’est populaire parce que c’est facile. C’est aussi dangereux parce que :
- Ça casse des composants qui dépendent du timing des transitions pour le nettoyage d’état.
- Ça peut provoquer des sauts soudains qui paraissent pires qu’un fondu court.
- Ça masque les sources de mouvement en dev parce que tout « marche en quelque sorte ».
Optez pour une approche ciblée : définissez des tokens de motion (durées/easings) et échangez-les selon la préférence. Désactivez globalement seulement les vraies boucles décoratives.
JS : lisez la préférence une fois, puis écoutez les changements
Utilisez window.matchMedia('(prefers-reduced-motion: reduce)'). Mais ne le faites pas dans dix fichiers. Enrobez-le.
Détail important : les navigateurs ont changé les API au fil du temps ; certains environnements supportent addEventListener('change', ...), les plus anciens utilisent addListener. Votre wrapper doit gérer les deux.
Frameworks de composants : évitez le « motion comme effet secondaire »
Dans React et consorts, le mouvement devient souvent un effet secondaire : un hook déclenche une transition au montage, une librairie anime des changements de layout, une « micro-interaction » se déclenche au hover.
Deux règles qui vous évitent des ennuis :
- Rendez les paramètres de motion des props explicites. Durée, easing et activation du mouvement ne doivent pas être codés en dur au fond d’un composant.
- N’inférez jamais le mouvement réduit à partir de la « performance du device ». Les utilisateurs demandent le mouvement réduit pour leur confort, pas parce que leur GPU est fatigué.
Défilement et navigation : traitez le smooth scroll comme un outil puissant
Le CSS scroll-behavior: smooth est tentant parce que c’est une ligne. C’est aussi une des manières les plus simples de violer le mouvement réduit.
Politique :
- Par défaut : autoriser le smooth scroll seulement pour des navigations déclenchées explicitement par l’utilisateur (par ex., cliquer « Aller à la section »), pas pour des événements de scroll arbitraires.
- Mode réduit : désactiver le smooth scroll.
Canvas/WebGL : « mouvement réduit » peut signifier « pause »
Si vous lancez une boucle de rendu toujours active, traitez le mode réduit comme un indice pour arrêter ou ralentir. Remplacez le mouvement d’arrière-plan par une image statique, ou rendez à la demande. Bonus : l’autonomie s’améliore, le ventilateur cesse de s’emballer et personne ne dépose un ticket pour vertige.
Blague #2 : Si votre spinner de chargement a besoin d’un support pour le mouvement réduit, félicitations — vous avez inventé une nouvelle forme de test de stress pour humains.
Tâches pratiques avec commandes (et décisions)
Voici des tâches pratiques que vous pouvez exécuter aujourd’hui. Chacune inclut : une commande, un exemple de sortie, ce que cela signifie, et la décision à prendre.
Task 1: Find CSS reduced-motion coverage in your repo
cr0x@server:~$ rg -n "prefers-reduced-motion" .
./src/styles/motion.css:12:@media (prefers-reduced-motion: reduce) {
./src/components/Carousel/carousel.css:88:@media (prefers-reduced-motion: reduce) {
Sens : Vous avez deux blocs reduced-motion explicites. Ça peut être bien — ou ça peut signifier que la plupart des composants l’ignorent.
Décision : Si moins que vos sources de motion cruciales apparaissent ici, planifiez un audit de motion (voir checklist). Le mouvement réduit doit être systématique, pas accessoire.
Task 2: Inventory CSS animations and transitions
cr0x@server:~$ rg -n "(animation:|animation-|transition:|transition-)" src
src/styles/base.css:41:transition: all 300ms ease;
src/styles/toast.css:9:animation: slideIn 450ms cubic-bezier(.2,.8,.2,1);
src/components/Hero/hero.css:22:animation: float 4s ease-in-out infinite;
Sens : Vous avez au moins une animation infinie (float) et au moins une dangereuse transition: all.
Décision : Les animations décoratives infinies doivent être désactivées en mode réduit. Remplacez transition: all par des propriétés explicites (opacity/transform), puis gérez les durées via des tokens de motion.
Task 3: Catch “scroll-behavior: smooth” landmines
cr0x@server:~$ rg -n "scroll-behavior:\s*smooth" src
src/styles/base.css:7:html { scroll-behavior: smooth; }
Sens : Le défilement fluide est global. C’est une violation classique du mouvement réduit.
Décision : Changez pour conditionnel : désactivé par défaut ou limité. En mode réduit, forcez scroll-behavior: auto.
Task 4: Locate requestAnimationFrame loops that may run forever
cr0x@server:~$ rg -n "requestAnimationFrame\(" src
src/visuals/background.ts:55:rafId = requestAnimationFrame(tick);
src/components/Chart/liveChart.ts:112:window.requestAnimationFrame(draw);
Sens : Il y a des animations/loops pilotées par JS.
Décision : Assurez-vous que ces boucles respectent le mouvement réduit : pause, rendu unique, ou passage à un rendu piloté par événements. Assurez aussi le nettoyage au démontage pour éviter les fuites.
Task 5: Find Framer Motion / animation library defaults
cr0x@server:~$ rg -n "(framer-motion|useReducedMotion|motion\.)" src
src/app/App.tsx:14:import { MotionConfig } from "framer-motion";
src/components/Modal/Modal.tsx:6:import { motion, useReducedMotion } from "framer-motion";
Sens : Vous utilisez une librairie qui a des opinions sur le motion. Bien — si elle est configurée de manière centrale.
Décision : Vérifiez qu’une configuration globale existe (ex., MotionConfig) et que des overrides au niveau composant ne réactivent pas le motion en mode réduit.
Task 6: Detect Lottie usage (often ignores reduced motion by default)
cr0x@server:~$ rg -n "(lottie|bodymovin)" src
src/components/EmptyState/EmptyState.tsx:3:import Lottie from "lottie-react";
src/components/PromoBanner/PromoBanner.tsx:8:import lottieData from "./promo.json";
Sens : Des animations Lottie sont dans l’UI produit, probablement en boucle.
Décision : En mode réduit : ne pas autoplay ; afficher une frame statique ou une image alternative. Traitez cela comme requis, pas « sympa à avoir ».
Task 7: Verify your design tokens include motion tokens
cr0x@server:~$ rg -n "(--duration|--easing|motion)" src/styles
src/styles/tokens.css:12:--duration-fast: 120ms;
src/styles/tokens.css:13:--duration-medium: 220ms;
src/styles/tokens.css:14:--duration-slow: 360ms;
Sens : Vous avez des variables CSS liées au motion. Bonne base.
Décision : Ajoutez des overrides pour le mode réduit pour ces variables au lieu d’essayer de tout zéroer avec !important. Exemple : raccourcir et supprimer les easings à rebond.
Task 8: Confirm Playwright tests can emulate reduced motion
cr0x@server:~$ rg -n "reducedMotion" tests
tests/e2e/login.spec.ts:9: await page.emulateMedia({ reducedMotion: "reduce" });
Sens : Au moins un test utilise l’émulation du mouvement réduit.
Décision : Élargissez la couverture aux pages riches en motion. Ajoutez un test qui affirme l’absence d’animations infinies et l’absence de comportement de smooth scrolling en mode réduit (autant que possible via le DOM/CSS).
Task 9: Quick check your bundle for smooth scroll polyfills
cr0x@server:~$ rg -n "(smoothscroll|scrollTo\(\{|behavior:\s*\"smooth\")" src
src/lib/navigation.ts:28:window.scrollTo({ top: 0, behavior: "smooth" });
Sens : Le JS force le smooth scroll, indépendamment du réglage utilisateur.
Décision : Gatez cet appel : si le mouvement réduit est activé, utilisez behavior: "auto" ou omettez behavior.
Task 10: Identify “transitionend/animationend” event dependencies
cr0x@server:~$ rg -n "(transitionend|animationend)" src
src/components/Drawer/Drawer.tsx:88:el.addEventListener("transitionend", onDone);
Sens : La logique du composant dépend des transitions qui se terminent.
Décision : En mode réduit, vous pouvez raccourcir les durées au point que les événements se comportent différemment, ou bien désactiver totalement les transitions. Ajoutez un chemin de code explicite : si le mouvement réduit est activé, appelez onDone() immédiatement (ou après une microtask), n’attendez pas des événements qui pourraient ne jamais se produire.
Task 11: Confirm third-party scripts that might animate overlays
cr0x@server:~$ ls -1 public/vendor
chat-widget.js
marketing-overlay.js
Sens : Des scripts vendors existent et peuvent injecter des animations hors du contrôle de votre CSS.
Décision : Auditez les vendors. S’ils ne supportent pas le mouvement réduit, enveloppez-les : désactivez l’autoplay, supprimez les overlays, ou chargez des configurations alternatives lorsque le mode réduit est activé.
Task 12: Local verification in Chromium via DevTools (emulation is not reality, but it helps)
cr0x@server:~$ chromium --user-data-dir=/tmp/chrome-prm --enable-features=WebContentsForceDark
[12874:12874:1229/101512.116955:INFO:chrome_main_delegate.cc(594)] Started
Sens : Vous avez lancé un profil Chromium propre pour éviter le bruit des extensions. (Le flag montré n’est pas à propos du motion ; l’idée est : isoler les variables.)
Décision : Dans le panneau Rendering de DevTools, émulez « prefers-reduced-motion » et inspectez visuellement les flux clés. Puis répétez avec le réglage OS réel activé pour repérer les différences.
Task 13: Smoke-test that you’re not globally forcing “no-preference” in CSS
cr0x@server:~$ rg -n "prefers-reduced-motion:\s*no-preference" src
src/styles/motion.css:33:@media (prefers-reduced-motion: no-preference) {
Sens : Vous avez du style explicite pour le mode par défaut. C’est acceptable.
Décision : Assurez-vous que votre bloc « no-preference » n’écrase pas accidentellement le mode réduit à cause de la spécificité. Préférez un override « reduce » qui l’emporte (et testez-le).
Task 14: Check for CSS animations that might run even when hidden
cr0x@server:~$ rg -n "infinite" src/styles src/components
src/components/Hero/hero.css:22:animation: float 4s ease-in-out infinite;
src/components/Background/bg.css:11:animation: shimmer 1.6s linear infinite;
Sens : Il existe des boucles infinies. Les effets shimmer sur les skeleton screens sont des coupables fréquents.
Décision : En mode réduit, transformez le shimmer en placeholder statique. Pour les floats des héros : figez. Votre CPU (et vos utilisateurs) vous remercieront.
Trois mini-histoires d’entreprise du terrain
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
Une équipe SaaS B2B de taille moyenne a livré une refonte avec une nouvelle barre latérale élégante. Le tiroir glissait, l’arrière-plan était flouté et l’overlay fondait. Ils avaient aussi un toggle de mouvement réduit — techniquement. Il était implémenté avec un snippet CSS global qui mettait les durées d’animation à presque zéro en mode réduit.
Sur le papier, cela semblait conforme. En pratique, le composant drawer écoutait transitionend pour déplacer le focus dans le tiroir et définir aria-hidden correctement sur le contenu en arrière-plan. Avec une durée effectivement nulle, l’événement ne se déclenchait pas de façon cohérente selon les navigateurs. Parfois il se déclenchait avant que le listener ne soit attaché. Parfois pas du tout.
Des utilisateurs avec le mouvement réduit activé ont commencé à signaler « navigation au clavier cassée » et « page bloquée ». Le support l’a escaladé en incident d’accessibilité. L’ingénierie l’a d’abord traité comme un cas mineur parce que « ça n’arrive qu’avec le mouvement réduit activé ». Cette phrase a mal vieilli en moins d’une heure.
Le correctif était ennuyeux et correct : la machine d’état du tiroir a cessé de dépendre des événements de transition comme source de vérité. Les transitions sont devenues cosmétiques. Si le mouvement réduit est activé, le chemin est déterministe : définir l’état DOM, mettre le focus, ignorer les animations et appeler le nettoyage de façon synchrone.
La mauvaise hypothèse n’était pas sur le CSS. C’était la croyance que le mouvement réduit est « juste des durées plus courtes ». Ce n’est pas ça. C’est un mode runtime distinct.
Mini-histoire 2 : L’optimisation qui s’est retournée contre eux
Une grande équipe produit grand public avait des soucis de performance sur appareils bas de gamme. Leur solution : remplacer plusieurs transitions CSS par un orchestrateur d’animations JavaScript pour pouvoir « regrouper les mises à jour » et « éviter le thrash de layout ». Ils ont aussi consolidé plusieurs micro-interactions dans une timeline d’animation partagée.
Les performances se sont améliorées dans les benchmarks. Les rapports utilisateurs se sont aggravés. L’orchestrateur utilisait des boucles requestAnimationFrame qui tournaient continuellement pour « la réactivité », même lorsque les animations étaient inactives. Il appliquait aussi le smooth scrolling par défaut quand l’utilisateur naviguait sur une page. Le support du mouvement réduit était implémenté uniquement en CSS, donc aucune logique JS d’animation ne le respectait.
Les utilisateurs avec le mouvement réduit activé voyaient encore de la parallaxe et le easing de scroll. Certains ont aussi subi une hausse de la consommation batterie parce que la boucle rAF continue maintenait la page « chaude ». L’équipe a d’abord blâmé l’OS. Puis le navigateur. Puis « peut-être les utilisateurs ont mal configuré quelque chose ». Classique.
Ils ont résolu le problème en faisant quelque chose qui semblait être un pas en arrière : ils ont remis la plupart des interactions en CSS (où la plateforme peut optimiser), et ont construit un module JS unique « préférence de motion » qui contrôle chaque point d’entrée d’animation. Ils ont aussi transformé la boucle rAF en modèle piloté par événements : ne tournant que lorsque l’animation est active.
Le retour de bâton n’était pas que les animations JS sont toujours mauvaises. C’était que l’optimisation ignorait l’exigence opérationnelle : le mouvement réduit doit être appliqué de façon cohérente à toutes les sources de mouvement.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une application web du secteur financier avait un processus de release strict. Pas glamour. L’équipe maintenait une petite « checklist contrat d’accessibilité » pour chaque changement d’interface. Un item : « Tester avec le mouvement réduit activé sur au moins un OS, et dans les E2E automatisés pour les flows fortement animés. »
Un designer a proposé un nouvel onboarding avec illustrations animées et un « tour guidé » qui faisait défiler. L’ingénierie l’a implémenté avec une librairie d’animations populaire et quelques fichiers Lottie. Pendant la pré-release, la QA a exécuté la checklist reduced motion et a constaté que le tour guidé continuait d’auto-défiler entre les étapes, même lorsque le mouvement réduit était activé.
Le correctif était simple : en mode réduit, le tour guidé a cessé d’avancer automatiquement et a remplacé les transitions de scroll par des sauts instantanés avec gestion claire du focus. Les Lottie ont été remplacées par des frames statiques. Pas de drame, pas d’incident, pas d’excuses publiques.
Rien de tout cela n’était ingénieux. La pratique a fonctionné parce qu’elle était répétable, appliquée et attachée aux releases comme une ceinture de sécurité. On ne la remarque pas jusqu’au moment où elle est vraiment nécessaire.
Erreurs courantes (symptômes → cause → correctif)
1) Symptom: “Reduced motion enabled, but parallax still happens”
Cause racine : La parallaxe est implémentée dans des handlers de scroll JS (ou une librairie) qui ne vérifient jamais la préférence.
Correctif : Gatez l’initialisation de la parallaxe sur la préférence. En mode réduit, affichez un arrière-plan statique et supprimez les listeners de scroll.
2) Symptom: “Reduced motion makes modals/drawers break (focus stuck, overlay weird)”
Cause racine : Les changements d’état dépendent des événements transitionend/animationend.
Correctif : Rendez l’état des composants déterministe ; traitez les animations comme optionnelles. En mode réduit, appelez les handlers de complétion directement et gérez le focus de manière synchrone.
3) Symptom: “Everything snaps instantly and feels worse”
Cause racine : Reset global des durées à 0ms. Les sauts soudains peuvent être désorientants.
Correctif : Raccourcissez plutôt qu’éliminez pour les transitions essentielles ; éliminez seulement le mouvement décoratif. Utilisez des tokens de motion et ajustez les easings.
4) Symptom: “Skeleton loaders shimmer forever even in reduce mode”
Cause racine : Animation CSS infinie non surchargée en mode réduit.
Correctif : Remplacez le shimmer par un placeholder statique en mode réduit. Envisagez un bloc de couleur subtil sans mouvement.
5) Symptom: “Smooth scrolling still triggers in reduce mode”
Cause racine : scroll-behavior: smooth défini globalement, ou JS scrollTo({behavior:"smooth"}) appelé sans condition.
Correctif : En mode réduit forcez scroll-behavior: auto, et gatez le comportement de scroll JS selon la préférence.
6) Symptom: “Reduced motion works on page load but not after OS toggle”
Cause racine : Préférence lue une seule fois et mise en cache ; pas d’écoute des changements.
Correctif : Abonnez-vous aux changements de media query et mettez à jour un store centralisé ; re-rendez les configs de motion en conséquence.
7) Symptom: “Third-party marketing banner keeps animating”
Cause racine : Widget vendeur qui ignore le mouvement réduit et injecte son propre CSS/JS.
Correctif : Chargez une configuration fournisseur adaptée au mouvement réduit, ou supprimez le widget en mode réduit. Faites-en une exigence de procurement.
8) Symptom: “CPU usage stays high even when nothing is happening”
Cause racine : Boucle rAF idle, boucle de rendu canvas, ou arrière-plan animé qui tourne en continu.
Correctif : Arrêtez le rendu quand ce n’est pas nécessaire ; en mode réduit, mettez par défaut en pause/statique. Assurez le nettoyage au démontage.
9) Symptom: “Reduced motion breaks analytics or A/B tests”
Cause racine : Le code d’expérience dépend des timings d’animation, ou mesure des événements liés aux transitions.
Correctif : Utilisez des événements d’état explicites plutôt que des événements d’animation. Instrumentez les événements business indépendamment du motion.
Checklists / plan étape par étape
Step 1: Do a motion inventory (one afternoon, high ROI)
- Recherchez les animations/transitions CSS et listez les composants qui les utilisent.
- Recherchez l’usage de rAF et les handlers de scroll.
- Listez les widgets tiers qui dessinent à l’écran (ads, chat, overlays marketing).
- Classez chaque élément de motion comme essentiel/utile/décoratif.
Step 2: Establish motion tokens (so you can change behavior without a repo-wide hunt)
- Créez des tokens de durée (fast/medium/slow) et des tokens d’easing (standard/emphasized).
- Remplacez les durées codées en dur et les « transition: all » par des tokens et des propriétés explicites.
- En mode réduit, raccourcissez les durées et supprimez les easings à rebond/overshoot.
Step 3: Implement reduced motion in CSS first
- Désactivez les animations décoratives infinies.
- Désactivez le smooth scrolling.
- Assurez-vous que les interactions clés continuent de transmettre l’état (anneaux de focus, changements de sélection).
Step 4: Implement reduced motion in JS with a single preference module
- Enrobez
matchMediadans un utilitaire qui expose une valeur courante et un mécanisme d’abonnement. - Gatez l’initialisation de la parallaxe, des animations autoplay et des boucles de rendu.
- Gérez les toggles runtime en mettant à jour l’état de votre app (ou rechargez les configs de motion).
Step 5: Make components resilient to “no animation”
- Supprimez la dépendance à
transitionend/animationendpour la correction fonctionnelle. - Assurez la gestion déterministe du focus et des états ARIA.
- Vérifiez que la suppression des animations ne change pas le layout de façon inattendue.
Step 6: Add automated tests (or your future self will re-live this bug)
- Ajoutez des E2E avec émulation du mouvement réduit pour les flows clés (auth, checkout, onboarding).
- Ajoutez des tests de régression qui affirment que les sources majeures de motion sont désactivées (ex., pas d’animations CSS infinies sur les pages critiques).
- Ajoutez une règle lint ou un check CI pour signaler
scroll-behavior: smoothet « transition: all » sauf approbation explicite.
Step 7: Operationalize it
- Ajoutez la vérification du mouvement réduit à la validation de sortie.
- Documentez votre « politique de motion » dans le repo (courte, appliquée, mise à jour).
- Assurez que les vendors tiers sont revus pour le support du mouvement réduit.
FAQ
Q1: Is reduced motion just for people with disabilities?
Non. La sensibilité au mouvement peut être situationnelle ou temporaire : migraines, médicaments, récupération après commotion, vertiges, fatigue, voire un trajet en train cahoteux. Concevrez pour des humains, pas pour des étiquettes.
Q2: Should we disable all transitions when reduced motion is enabled?
Désactivez le motion décoratif et les boucles infinies. Pour les transitions essentielles, préférez des changements courts et subtils (souvent l’opacité) plutôt que des mouvements dramatiques. L’objectif n’est pas « pas de mouvement », mais « pas d’absence de feedback ».
Q3: Is “duration: 0ms” a valid strategy?
C’est un instrument brutal. Ça peut casser des composants dépendant d’événements d’animation ou de timing, et créer des snaps brutaux. Utilisez des tokens et des chemins logiques explicites pour garantir la correction.
Q4: How do we handle smooth scrolling?
Désactivez le smooth scroll en mode réduit. Évitez aussi de l’activer globalement. Si vous devez l’utiliser pour un lien de saut, gatez le comportement en JS et gardez-le déclenché par l’utilisateur.
Q5: What about micro-interactions like hover effects?
Les effets hover peuvent rester s’ils sont subtils et sans mouvement (couleur/opacité). Évitez les translations ou rotations déclenchées au hover en mode réduit, surtout sur des UI denses où le hover est fréquent.
Q6: Do we need to respond to preference changes while the app is open?
Oui. Les utilisateurs peuvent basculer les réglages OS sans redémarrer l’onglet. Écoutez les changements de media query et mettez à jour votre configuration de motion.
Q7: How do we deal with animation libraries?
Choisissez des librairies qui exposent un mode reduced-motion ou acceptent des paramètres. Configurez-les de manière centrale. Puis appliquez la règle : aucun composant ne doit outrepasser le comportement reduced-motion pour réactiver le motion.
Q8: What’s the quickest way to catch reduced motion regressions?
Ajoutez un test smoke E2E qui charge une page riche en motion avec l’émulation du mouvement réduit et qui affirme que les éléments clés n’animent pas (ou que les classes d’animation sont absentes). Associez cela à une vérification manuelle OS-level dans la QA de release.
Q9: If reduced motion is on, can we still use spinners?
Oui, mais privilégiez des indicateurs non-rotationnels : barres de progression, points qui apparaissent sans mouvement, ou texte statique « Chargement… ». Si vous gardez un spinner, arrêtez-le en mode réduit ou passez à un fade subtil.
Conclusion : prochaines étapes à réaliser cette semaine
Le support du mouvement réduit n’est pas un élément de « finition ». C’est un mode runtime qui affecte la correction, la performance et la confiance des utilisateurs. Implémentez-le comme toute exigence opérationnelle : de façon centrale, testable, et avec un plan pour le chaos des tiers.
Prochaines étapes pratiques :
- Exécutez les recherches de repo ci-dessus et construisez un inventaire des motions.
- Supprimez le smooth scroll global et les animations décoratives infinies en mode réduit.
- Implémentez un module JS unique pour la préférence et reliez-le aux points d’entrée d’animation.
- Corrigez les composants qui dépendent d’événements d’animation pour fonctionner correctement.
- Ajoutez au moins un test E2E mouvement réduit pour votre flux utilisateur principal.
- Ajoutez un item à la checklist de release pour éviter une régression silencieuse le trimestre suivant.
Si vous ne faites qu’une chose : arrêtez de traiter le mouvement comme décoration. En production, le mouvement est un comportement. Le comportement a besoin de contrôles.