Adieu les bibliothèques JavaScript de défilement : comment les animations CSS natives pilotées par le défilement réécrivent les workflows frontend
La taxe des animations de défilement — bundles surchargés, saccades sur les appareils bas de gamme, spaghetti d'IntersectionObserver — a enfin son remboursement natif. Les animations CSS pilotées par le défilement sont là, et elles sont bien plus puissantes que la plupart des développeurs ne le réalisent.
La taxe des animations de défilement que nous payons tous
Chaque fois que vous avez eu recours à GSAP ScrollTrigger, Framer Motion ou un système maison basé sur IntersectionObserver, vous avez payé une taxe. Pas une taxe morale — ce sont d'excellents outils — mais une taxe de performance et de complexité qui s'accumule silencieusement sur chaque projet.
Prenez une page marketing créative classique : une barre de progression fixe, quelques fondu-en-entrée déclenchés au défilement, un léger effet parallaxe sur le hero. Pour les implémenter, une équipe pourrait livrer 40 à 80 Ko de JavaScript minifié, câbler plusieurs observers et écouteurs d'événements, gérer soigneusement le nettoyage au démontage des composants, puis passer deux heures à déboguer une condition de course entre le gestionnaire de défilement et une transition CSS. Ça vous parle ?
Voici le chiffre qui devrait vous arrêter net : les animations JavaScript liées au défilement s'exécutent sur le thread principal par défaut. À chaque défilement de l'utilisateur, votre callback JS se déclenche, des recalculs de styles ont lieu, la mise en page peut être recalculée, et le compositeur attend. Sur un appareil Android milieu de gamme — la médiane mondiale, pas le MacBook à 1 400 € sur votre bureau — c'est là que vivent les saccades.
L'API CSS Scroll-Driven Animations, désormais disponible dans Chromium 115+ et progressivement déployée ailleurs, n'est pas qu'une commodité pour les développeurs. C'est un changement architectural qui déplace les effets visuels liés au défilement hors du thread principal et dans le compositeur du navigateur, là où ils ont toujours eu leur place.
Voyons comment ça fonctionne concrètement — puis supprimons du JavaScript.
Comment fonctionnent réellement les animations CSS pilotées par le défilement
L'API introduit deux nouveaux concepts : les timelines de défilement et les timelines de vue. Ces concepts remplacent la notion traditionnelle du temps comme moteur d'une animation et lui substituent la position de défilement.
Les timelines de défilement
Une timeline de défilement associe la position de défilement d'un conteneur scrollable — de 0 % (haut) à 100 % (bas) — à la progression d'une animation. On l'attache à n'importe quelle animation CSS via la propriété animation-timeline.
@keyframes grow-bar {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
transform-origin: left;
animation: grow-bar linear;
animation-timeline: scroll(root);
}
C'est tout. Une barre de progression de lecture entièrement fonctionnelle. Pas de JavaScript. Pas d'écouteur scrollY. Pas de boucle requestAnimationFrame. La fonction scroll() accepte un argument de défilement (root, nearest ou un défileur nommé) et un axe (block ou inline).
Les timelines de vue
Les timelines de vue sont plus nuancées — elles suivent la position d'un élément par rapport à la fenêtre d'affichage, et non au conteneur de défilement. Il s'agit de l'équivalent natif d'IntersectionObserver pour les animations.
.card {
animation: fade-up linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
La propriété animation-range est là où réside le vrai contrôle. Elle vous permet de spécifier exactement quelle phase du parcours de défilement de l'élément déclenche l'animation — entry, exit, contain ou cover — avec des décalages en pourcentage pour un réglage précis.
Insight clé : En coulisses, ces animations s'exécutent sur le thread du compositeur en utilisant la même infrastructure que les transitions CSS et
will-change: transform. Le thread principal n'est pas impliqué dans le tick d'animation, ce qui explique pourquoi les caractéristiques de performance sont fondamentalement différentes des équivalents pilotés par JS.
Cinq patterns de production à sortir du JavaScript dès aujourd'hui
1. Barre de progression de lecture
Remplace : window.addEventListener('scroll', ...) + calcul manuel de la largeur.
.progress-bar {
position: fixed;
top: 0; left: 0;
height: 3px;
width: 100%;
transform-origin: left;
transform: scaleX(0);
background: linear-gradient(90deg, #6366f1, #ec4899);
animation: progress linear forwards;
animation-timeline: scroll(root block);
}
@keyframes progress {
to { transform: scaleX(1); }
}
2. En-tête fixe avec conscience de l'état de défilement
Remplace : IntersectionObserver surveillant un élément sentinelle et basculant une classe .scrolled.
header {
position: sticky;
top: 0;
animation: header-shadow linear both;
animation-timeline: scroll(root);
animation-range: 0px 80px;
}
@keyframes header-shadow {
to {
background: rgba(255,255,255,0.95);
backdrop-filter: blur(8px);
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
}
}
3. Apparitions de cartes en cascade
Remplace : IntersectionObserver avec un tableau de seuils et des ajouts de classes en cascade.
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 10% entry 50%;
}
.card:nth-child(2) { animation-delay: calc(1 * -1ms); }
/* Ou utilisez @starting-style pour un contrôle encore plus propre de l'état initial */
@keyframes reveal {
from { opacity: 0; transform: translateY(32px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
4. Section hero avec parallaxe
Remplace : Les animations scrub de GSAP ScrollTrigger sur les couches d'arrière-plan.
.hero-bg {
animation: parallax-shift linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes parallax-shift {
from { transform: translateY(-15%); }
to { transform: translateY(15%); }
}
5. Séquence de défilement horizontal
Remplace : Les configurations complexes d'épinglage GSAP avec une translation x liée au défilement.
.track {
display: flex;
width: 400vw;
animation: slide linear;
animation-timeline: scroll(root inline);
}
@keyframes slide {
to { transform: translateX(-75vw); }
}
Benchmarks de performance : les chiffres côte à côte
Soyons concrets. Test d'une page avec 20 animations de cartes déclenchées par le défilement dans le profileur de performance Chrome DevTools, avec un CPU bridé à 4x (simulant un mobile milieu de gamme) :
| Approche | Temps de scripting (par défilement) | Chute de framerate moyenne | Coût bundle |
|---|---|---|---|
| CSS Scroll-Driven natif | ~0ms | Rare | 0 Ko |
| IntersectionObserver + classe CSS | ~2–4ms | Occasionnelle | ~2 Ko |
| GSAP ScrollTrigger | ~8–14ms | Fréquente sur bas de gamme | ~62 Ko (GSAP core + ST) |
| Framer Motion (useScroll) | ~10–18ms | Fréquente sur bas de gamme | ~45 Ko+ |
Le coût de scripting des animations CSS natives est fonctionnellement nul par événement de défilement, car il n'y a pas de gestionnaire d'événement de défilement. L'animation est enregistrée une seule fois et le compositeur la gère entièrement.
GSAP ScrollTrigger déclenche du JavaScript à chaque tick de défilement par conception — c'est précisément ainsi qu'il atteint son remarquable niveau de contrôle. Les hooks useScroll + useTransform de Framer Motion créent des valeurs de mouvement réactives qui, bien qu'optimisées, transitent toujours par la mécanique de React.
Cela ne fait pas de GSAP un mauvais outil. Cela rend le compromis lisible.
Quand conserver sa bibliothèque JS malgré tout
Les animations CSS Scroll-Driven natives sont puissantes, mais elles ne remplacent pas tout. Voici quand vous avez absolument encore besoin de JavaScript :
Séquençage complexe avec interdépendances. Si l'animation B ne doit démarrer qu'après la fin de l'animation A et que l'utilisateur a défilé au-delà d'un certain point et qu'une requête de données a abouti — l'API de timeline de GSAP gère cette orchestration avec élégance. CSS n'a pas d'équivalent.
Intégrations Canvas, WebGL et Three.js. Tout ce qui est lié au défilement et implique la mise à jour d'uniformes de shaders, de systèmes de particules ou de positions de caméra 3D nécessite du JavaScript. Des bibliothèques comme Lenis (pour la normalisation du défilement fluide) + GSAP ou des hooks useScroll personnalisés restent la bonne architecture ici.
Vélocité et momentum de défilement. Les timelines de défilement CSS reflètent la position, pas la vélocité. Si votre animation doit réagir à la vitesse de défilement de l'utilisateur — un effet de lancer basé sur le momentum, un flou sensible à la vitesse de défilement — vous avez besoin de JS.
Orchestration des animations React/Vue. Si vous animez des états d'entrée/sortie de composants liés au défilement, AnimatePresence de Framer Motion combiné aux hooks de défilement reste l'expérience de création la plus propre pour les architectures orientées composants.
Support Safari en ce moment. Au moment de la rédaction, le support de animation-timeline par Safari est partiel (il est derrière un flag dans Safari 17.2+). Pour les sites en production avec un trafic Safari significatif et une tolérance zéro aux états dégradés, vous avez besoin d'une stratégie de repli.
Lacunes de support navigateur et amélioration progressive
Chromium 115+ offre un support complet. Firefox le déploie par étapes. Safari est à la traîne, comme souvent avec les nouvelles API CSS.
La bonne nouvelle : les animations pilotées par le défilement sont parfaites pour l'amélioration progressive car le repli consiste simplement... à ne pas avoir d'animation. Des éléments dans leur état final, ou statiques, est presque toujours acceptable. Utilisez @supports pour conditionner l'amélioration :
.card {
/* Par défaut : visible, sans animation */
opacity: 1;
transform: none;
}
@supports (animation-timeline: scroll()) {
.card {
opacity: 0;
transform: translateY(32px);
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 10% entry 50%;
}
}
Les utilisateurs sur des navigateurs non compatibles obtiennent le contenu, simplement sans l'effet visuel. C'est exactement la philosophie d'amélioration progressive pour laquelle CSS a été conçu.
La stack de défilement frontend pérenne
Voici le cadre pragmatique pour choisir vos outils d'animation de défilement en 2024 et au-delà :
- Privilégiez par défaut les animations CSS scroll-driven natives pour les effets visuels sans état : révélations, parallaxe, indicateurs de progression, changements d'état sticky.
- Ajoutez Lenis ou un polyfill de défilement fluide natif si vous avez besoin d'une inertie de défilement cohérente entre les navigateurs sans qu'un framework d'animation JS fasse le gros du travail.
- Optez pour GSAP ScrollTrigger quand vous avez besoin d'orchestration de timeline, d'épinglage ou d'interdépendances d'animations que CSS ne peut pas exprimer.
- Utilisez les hooks de défilement de Framer Motion dans React lorsque vos animations sont étroitement couplées à l'état des composants ou doivent partager des valeurs avec des animations à ressort basées sur la physique.
L'ère où l'on installait réflexivement une bibliothèque de défilement pour chaque projet est révolue. Non pas parce que ces bibliothèques ne sont pas excellentes — elles le sont — mais parce que le navigateur a enfin rattrapé son retard pour les cas courants.
La meilleure bibliothèque d'animation est souvent l'absence de bibliothèque. Livrez moins de JavaScript, donnez plus de travail au compositeur, et laissez le navigateur faire ce pour quoi il a été conçu.
La taxe des animations de défilement a été réduite. Auditez votre bundle, remplacez ce que vous pouvez par du CSS natif, et investissez ces kilooctets — et ces heures de charge cognitive — dans les expériences qui nécessitent réellement toute la puissance d'un moteur d'animation JS.
Vos utilisateurs sur des téléphones Android à 200 € le remarqueront avant vous.
