Motion design : des micro-interactions qui ont du sens
Objectifs de ce chapitre
- Régler durée, courbe et propriété de chaque animation
- Chorégraphier les apparitions et les transitions au scroll
- Respecter performance et prefers-reduced-motion
Une page parfaite, mais figée
La landing Sereno est désormais typographiée, thémée clair et sombre, accessible. Le client la fait défiler en silence, puis lâche : « C'est très beau. Mais c'est… immobile. On dirait une affiche. » Le directeur de Studio Mango traduit : il manque le motion — cette couche de mouvement qui fait qu'une interface semble vivante et répond sous les doigts. Le chapitre 5 a posé la règle (courtes, douces, signifiantes) ; aujourd'hui, tu construis le système de mouvement complet, avec la même rigueur que pour les couleurs et la typo.
Car c'est le point clé : le motion se conçoit en tokens et en règles, pas en effets ajoutés au coup par coup. Une interface où chaque élément bouge à sa propre vitesse avec sa propre courbe paraît bricolée — exactement comme une page aux espacements aléatoires. Un petit nombre de durées, une ou deux courbes signature, des règles d'usage : voilà ce que tu vas livrer.
Le mouvement est un langage fonctionnel
Avant les réglages, le pourquoi. Une animation bien conçue remplit l'un de trois rôles. Le feedback : confirmer qu'une action a été perçue — le bouton qui s'enfonce légèrement au clic dit « je t'ai entendu ». L'orientation : montrer d'où vient et où va un élément — le panneau qui glisse depuis la droite indique qu'il se refermera vers la droite. La continuité : lier deux états d'une interface pour que le changement ne soit pas un saut brutal — la carte qui s'étend en douceur plutôt qu'une modale qui surgit de nulle part.
Toute animation qui ne remplit aucun de ces rôles est décorative — du bruit. Pour une app de méditation, le critère est encore plus strict : chaque mouvement doit apaiser, jamais distraire. Demande-toi pour chaque animation : « qu'est-ce qu'elle dit à l'utilisateur ? ». Si la réponse est « rien, mais c'est joli », supprime-la. C'est la version motion de la chasse au générique.
Les trois réglages : durée, courbe, propriété
Première décision : la durée, proportionnelle à l'ampleur du changement. Les micro-feedbacks (survol, focus, case cochée) vivent entre 100 et 200 ms — plus court paraît instantané, plus long paraît mou. Les transitions moyennes (apparition d'une carte, dépliage d'un accordéon) entre 200 et 300 ms. Les grands mouvements (panneau latéral, changement de page) entre 300 et 500 ms — au-delà, l'utilisateur attend l'interface, et l'attente répétée devient de l'agacement.
Deuxième décision : la courbe d'accélération (easing). La règle physique : ce qui entre à l'écran décélère (ease-out — l'élément arrive vite et se pose en douceur), ce qui sort accélère (ease-in — il s'échappe), ce qui se déplace sur place fait les deux (ease-in-out). Le linear est réservé aux rotations continues type spinner. Et pour donner une signature à Sereno, on définit une courbe cubic-bezier maison, douce et légèrement amortie, utilisée partout — c'est l'équivalent motion de la police de titres.
:root {
/* Tokens de mouvement — le système, pas des effets isolés */
--duration-fast: 150ms; /* micro-feedback : hover, focus */
--duration-base: 250ms; /* transitions moyennes : cartes, accordéons */
--duration-slow: 400ms; /* grands mouvements : panneaux, pages */
--ease-out-soft: cubic-bezier(0.25, 0.8, 0.35, 1); /* la signature Sereno */
--ease-in-soft: cubic-bezier(0.55, 0, 0.7, 0.4);
}
.btn {
transition:
background-color var(--duration-fast) var(--ease-out-soft),
transform var(--duration-fast) var(--ease-out-soft),
box-shadow var(--duration-fast) var(--ease-out-soft);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(0.98); }--ease-out-soft) et interdis les easings improvisés dans les prompts, exactement comme tu interdis les couleurs en dur.Performance : n’anime que transform et opacity
Troisième décision, la plus technique : la propriété animée. Toutes les propriétés CSS ne se valent pas. Animer width, height, top ou margin force le navigateur à recalculer la mise en page de toute la zone à chaque image — sur un téléphone moyen, l'animation saccade. Animer transform (translation, échelle, rotation) et opacity est traité directement par le processeur graphique : fluide à 60 images par seconde, même sur du matériel modeste.
La traduction pratique : un élément qui « monte » au survol utilise transform: translateY(-2px), jamais top: -2px. Un panneau qui s'ouvre glisse avec translateX, il ne change pas sa width. Une apparition combine opacity et une petite translation. Ajoute cette contrainte à tous tes prompts de motion — c'est une de ces exigences que l'IA respecte parfaitement quand on la formule, et oublie une fois sur deux sinon.
will-change : utile ponctuellement pour préparer une animation lourde, il consomme de la mémoire s'il est posé partout « par précaution ». La règle : ne l'ajouter que si une animation saccade réellement, et le retirer après l'animation.Chorégraphie : ordonner les apparitions
Une page n'apparaît pas d'un bloc : elle se raconte. La chorégraphie consiste à ordonner les apparitions selon la hiérarchie de lecture : le titre d'abord, le sous-titre ensuite, le CTA enfin — chacun décalé de 50 à 80 ms par rapport au précédent. Ce décalage (le « stagger ») est assez court pour que l'ensemble paraisse fluide, assez long pour que l'œil suive le fil. Les trois cartes de bénéfices apparaissent en cascade gauche-droite, pas toutes en même temps.
flowchart LR L["Chargement de la section"] --> T["Titre : apparition immédiate"] T --> ST["Sous-titre : décalage de 60 ms"] ST --> CTA["CTA : décalage de 120 ms"] CTA --> C1["Carte 1"] C1 --> C2["Carte 2 : plus 70 ms"] C2 --> C3["Carte 3 : plus 140 ms"]
Apparitions au scroll, avec sobriété
Le grand classique des landing pages : les sections qui se révèlent en douceur à mesure qu'on défile. Bien dosé, l'effet rythme la lecture ; surdosé, il transforme la page en parc d'attractions. Les règles de sobriété : une distance de déplacement courte (12 à 16 px, pas 100), une apparition unique (l'élément reste visible une fois révélé, il ne rejoue pas l'animation à chaque passage), et un seul type d'effet pour toute la page — fondu + légère montée, point. Pas de zoom ici, de rotation là, de rebond ailleurs.
/* CSS : l'état initial et l'état révélé */
.reveal {
opacity: 0;
transform: translateY(14px);
transition: opacity var(--duration-slow) var(--ease-out-soft),
transform var(--duration-slow) var(--ease-out-soft);
}
.reveal.is-visible { opacity: 1; transform: none; }// JS : IntersectionObserver — révèle une seule fois
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target); // une seule fois
}
}
}, { threshold: 0.15 });
document.querySelectorAll('.reveal').forEach((el) => observer.observe(el));Ajoute un système de motion complet à la landing Sereno (code ci-joint) : - tokens : --duration-fast 150ms, --duration-base 250ms, --duration-slow 400ms, courbe signature cubic-bezier(0.25, 0.8, 0.35, 1) - micro-feedbacks : hover et active sur boutons, cartes et liens (transform + ombre, jamais top/width) - chorégraphie de la hero : titre, puis sous-titre (+60ms), puis CTA (+120ms), en fondu + translateY(14px) - apparitions au scroll : sections en fondu + montée de 14px, une seule fois, via IntersectionObserver - CONTRAINTES : n'anime que transform et opacity ; aucune animation de plus de 500ms ; aucun rebond ni effet élastique - ajoute le bloc @media (prefers-reduced-motion: reduce) qui désactive translations et stagger en gardant de simples fondus rapides Donne le CSS et le JS séparés, commentés.
prefers-reduced-motion : non négociable
Une partie des utilisateurs souffre de troubles vestibulaires : les mouvements d'interface — parallaxe, glissements amples, zooms — leur causent vertiges et nausées réels. D'autres préfèrent simplement une interface calme. Tous peuvent activer « réduire les animations » dans leur système, et ton CSS doit l'écouter via la media query prefers-reduced-motion: reduce. Réduire ne veut pas dire tout casser : on remplace les translations par de simples fondus courts, et on supprime les animations purement décoratives. L'interface reste vivante, elle cesse de bouger dans l'espace.
@media (prefers-reduced-motion: reduce) {
.reveal {
transform: none; /* plus de déplacement spatial */
transition: opacity 120ms ease; /* un simple fondu court */
}
.btn:hover, .btn:active { transform: none; }
}Pour une app de méditation, l'ironie serait cruelle : donner la nausée à l'utilisateur venu chercher le calme. Intègre la media query dès le premier prompt de motion (comme ci-dessus), pas en correctif de dernière minute — et ajoute ce point à ta liste de vérification de livraison, au même rang que les contrastes.
Quand ne pas animer
Audit motion de la landing Sereno (code ci-joint). Dresse un tableau exhaustif de chaque animation : - élément concerné, déclencheur, durée, courbe, propriétés animées - son rôle : feedback, orientation ou continuité — écris « AUCUN » si elle est purement décorative - conformité : propriétés limitées à transform/opacity ? durée sous 500 ms ? comportement défini sous prefers-reduced-motion ? Puis : 1. liste les animations à SUPPRIMER (rôle AUCUN) avec une phrase de justification 2. liste les non-conformités techniques et leur correctif 3. vérifie que toutes les durées et courbes proviennent des tokens --duration-* et --ease-*, et signale toute valeur en dur
Termine par l'inventaire critique : demande à l'IA la liste de toutes les animations de la page avec leur durée, leur propriété et leur rôle (feedback, orientation, continuité). Toute ligne dont le rôle reste vide est candidate à la suppression. Tu verras presque toujours le même résultat : la page finale compte moins d'animations que la version intermédiaire, mais chacune est juste. Le motion premium ressemble au montage d'un bon film — on ne remarque pas les coupes, on sent juste que tout coule. Le client ne dira pas « belles animations » ; il dira « c'est apaisant », et c'est exactement la mission.
Contexte
« C'est immobile », a dit le client. Tu as une après-midi pour ajouter la couche de mouvement à la landing Sereno : micro-feedbacks, chorégraphie de la hero, révélations au scroll — le tout fluide sur un téléphone d'entrée de gamme et respectueux de prefers-reduced-motion. Le directeur sera intraitable sur un point : pas un seul mouvement gratuit.
Consignes
- Définis tes tokens de motion : trois durées (150/250/400 ms) et une courbe signature cubic-bezier, ajoutés au design system.
- Ajoute les micro-feedbacks aux boutons, cartes et liens — uniquement transform et opacity, transitions de 150 ms.
- Chorégraphie la hero : titre, sous-titre, CTA en cascade avec un stagger de 60 ms, en fondu + montée de 14 px.
- Mets en place les révélations au scroll avec IntersectionObserver : un seul type d’effet, une seule fois par élément.
- Ajoute le bloc prefers-reduced-motion qui remplace tout déplacement par un fondu court, et teste-le en activant l’option dans ton système.
- Demande l’inventaire critique : liste de chaque animation avec son rôle (feedback, orientation, continuité) — supprime toute ligne sans rôle.
En résumé
- Le motion se conçoit en système : tokens de durées, courbe signature, règles d’usage — pas en effets isolés.
- Chaque animation remplit un rôle : feedback, orientation ou continuité — sinon c’est du bruit à supprimer.
- Durées : 100-200 ms pour les micro-feedbacks, 200-300 ms pour les transitions, 300-500 ms pour les grands mouvements.
- Ease-out pour ce qui entre, ease-in pour ce qui sort, ease-in-out pour ce qui se déplace ; linear réservé aux rotations continues.
- On n’anime que transform et opacity : les autres propriétés font saccader les téléphones modestes.
- La chorégraphie suit la hiérarchie de lecture avec un stagger de 50-80 ms ; au scroll : un seul effet, une seule fois, 12-16 px.
- prefers-reduced-motion est non négociable : remplacer les déplacements par des fondus courts, dès le premier prompt.
Quiz — vérifie ta compréhension
1. Quels sont les trois rôles légitimes d’une animation d’interface ?
2. Quelle durée convient à un micro-feedback de survol ?
3. Pourquoi n’animer que transform et opacity ?
4. Quelle courbe d’accélération pour un élément qui entre à l’écran ?
5. Que doit faire le bloc prefers-reduced-motion: reduce ?
6. Qu’est-ce qu’un « stagger » dans une chorégraphie d’apparition ?