Au-delà du parallaxe : comment les animations CSS pilotées par le défilement rendent les bibliothèques JS obsolètes
L'API native CSS Scroll-Driven Animations a discrètement fait son apparition dans les navigateurs, et elle est suffisamment performante pour remplacer GSAP ScrollTrigger dans la majorité des effets que vous intégrez. Voici ce que cela signifie concrètement pour votre stack.
Au-delà du parallaxe : comment les animations CSS pilotées par le défilement rendent les bibliothèques JS obsolètes
Chaque fois que vous faites npm install gsap pour un fade au défilement ou une barre de progression sticky, vous payez une taxe. Une taxe de 67 Ko (minifiés), une taxe d'analyse et d'exécution, et de plus en plus — une taxe inutile. L'API CSS Scroll-Driven Animations se construit vers une maturité de production depuis deux ans, et depuis mi-2024, elle affiche plus de 75 % de couverture mondiale des navigateurs, avec Chrome, Edge et Opera entièrement compatibles. Safari et Firefox rattrapent rapidement leur retard.
Ceci n'est pas un article sur une « expérience sympa ». C'est une conversation franche entre développeurs sur le moment où il faut se tourner vers la plateforme native et celui où une bibliothèque justifie encore sa place.
La taxe des animations de défilement JavaScript que nous avons acceptée
Soyons honnêtes sur les raisons pour lesquelles les bibliothèques JavaScript de défilement sont devenues la solution par défaut. Quand ScrollTrigger de GSAP est sorti, c'était véritablement une révélation — fluide, fiable, avec un épinglage qui fonctionnait vraiment, un scrubbing d'une douceur remarquable. Framer Motion a apporté la même puissance à React avec une API orientée composants. Nous avons eu recours à ces outils parce que l'alternative était un enchevêtrement fragile de callbacks IntersectionObserver, de boucles requestAnimationFrame et d'écouteurs d'événements scroll qui massacraient les performances sur les appareils Android d'entrée de gamme.
Mais cette alternative a changé.
« La meilleure optimisation des performances est celle que le navigateur gère avant même que votre JavaScript ne soit analysé. »
Les animations pilotées par le défilement s'exécutant nativement en CSS tournent hors du thread principal — là où vivent les transitions CSS et les animations transform. Aucun temps d'analyse JS. Aucune surcharge d'écouteur d'événements. Aucun saccade quand votre thread principal est occupé à hydrater un arbre React.
La question n'est pas de savoir si le natif est meilleur en théorie. C'est de savoir s'il est suffisamment bon en pratique pour couvrir vos cas d'usage. Spoiler : pour la plupart des projets d'agence, c'est le cas.
L'API native expliquée : animation-timeline sans le jargon
La spécification Scroll-Driven Animations introduit deux types de timelines principaux : les scroll timelines et les view timelines. Ils semblent similaires mais résolvent des problèmes différents.
Scroll Timeline : liée à un conteneur de défilement
Une timeline scroll() progresse de 0 % à 100 % au fur et à mesure qu'un conteneur de défilement se déplace du haut vers le bas. Le cas d'usage classique : cette barre de progression de lecture en haut d'un article.
@keyframes grow-bar {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
animation: grow-bar linear;
animation-timeline: scroll(root block);
transform-origin: left;
}
C'est tout. Pas de window.addEventListener('scroll'). Pas de requestAnimationFrame. Pas de calcul element.scrollTop / document.body.scrollHeight. Le navigateur prend entièrement en charge le calcul.
La fonction scroll() accepte deux arguments : le scroller (root, nearest, ou un scroll-timeline-name nommé) et l'axe (block, inline, x, y).
View Timeline : liée à la visibilité d'un élément
C'est là que les choses deviennent vraiment passionnantes pour les développeurs créatifs. Une timeline view() progresse en fonction de la position d'un élément à l'intérieur du viewport de son conteneur de défilement — et non de la position de défilement du document. Pensez-y comme un IntersectionObserver boosté aux stéroïdes, avec un contrôle par keyframes.
@keyframes fade-up {
entry 0% { opacity: 0; transform: translateY(40px); }
entry 100% { opacity: 1; transform: translateY(0); }
}
.card {
animation: fade-up linear both;
animation-timeline: view();
}
Les mots-clés de plage (entry, exit, contain, cover) vous permettent de spécifier quelle phase du parcours de défilement de l'élément pilote l'animation. entry se déclenche à l'entrée de l'élément dans le viewport. exit se déclenche à sa sortie. contain est actif tant qu'il est entièrement visible. C'est cette API qui remplace directement le cas d'usage ScrollTrigger le plus courant — les animations d'apparition déclenchées par le défilement.
Timelines nommées pour une chorégraphie complexe
Lorsque vous avez besoin que la position de défilement d'un élément pilote l'animation d'un autre élément (parallaxe classique), vous utilisez des timelines nommées déclarées avec scroll-timeline-name et consommées par un élément séparé :
.scroll-container {
scroll-timeline-name: --my-timeline;
overflow-y: scroll;
}
.parallax-layer {
animation: drift linear;
animation-timeline: --my-timeline;
}
Comparatif de performances : natif vs bibliothèque à grande échelle
Les comparaisons de performances entre les animations CSS natives et les bibliothèques JS sont nuancées, mais les données penchent fortement en faveur du natif pour les bons types de charge de travail.
Dans des tests reproduits à partir des propres benchmarks de l'équipe Chrome DevTools et des travaux indépendants d'Adam Argyle (défenseur CSS chez Google), des scénarios courants dressent un tableau clair :
- Animation de barre de progression : le CSS natif utilise ~0 ms de temps d'exécution JS. L'équivalent GSAP ScrollTrigger : 2–5 ms par événement de défilement sur un appareil de milieu de gamme.
- Animation d'entrée décalée sur 20 éléments : CSS natif avec les timelines
view()— impact sur le thread principal : négligeable. GSAP ScrollTrigger avec le même effet : 8–15 ms par événement, provoquant souvent des chutes d'images lors de défilements rapides. - Couches de fond en parallaxe (3 couches) : ici l'écart se réduit. CSS natif : ~1–2 ms de travail composite. GSAP avec des indications
will-change: ~3–5 ms. Pas catastrophique, mais mesurable.
L'insight clé : les animations CSS pilotées par le défilement s'exécutent pendant les phases de style et de composition du navigateur, et non pendant l'exécution JS. Sur une page qui effectue déjà un travail JavaScript conséquent (routage SPA, chargement différé, appels analytics), cette séparation fait toute la différence entre 60 fps et 45 fps sur un Pixel 5.
Des sites comme Linear.app et les pages marketing de Vercel ont publiquement migré vers le CSS natif pour leurs effets de défilement, précisément parce que la voie pilotée par le compositeur élimine entièrement le problème du « saccade au scroll sous charge ».
Là où JavaScript l'emporte encore (soyons honnêtes)
Cet article serait malhonnête s'il ne reconnaissait pas les domaines où GSAP, Motion One et Framer Motion ont encore une place légitime.
1. Séquençage de timelines complexes avec callbacks
Si vous devez déclencher une requête fetch quand une animation atteint 50 %, désactiver un bouton pendant une transition liée au défilement, ou enchaîner des effets avec un chaînage de type then() — le CSS ne peut pas toucher à ça. L'API de timeline de GSAP avec les callbacks onUpdate et onComplete reste inégalée pour les animations à état.
2. Animations physiques et à ressort
L'easing linear() en CSS nous a rapprochés, mais la physique des ressorts qui réagit dynamiquement à la vélocité (pensez au useSpring de Framer Motion ou au plugin inertia de GSAP) reste encore du domaine de JavaScript.
3. Intégration Canvas et WebGL Si votre défilement pilote une scène Three.js, un canvas Pixi.js, ou des uniformes de shaders personnalisés — vous êtes en territoire JS, point final. Des bibliothèques comme Lenis pour la normalisation du défilement fluide ont toujours leur sens ici en tant que couche d'entrée du défilement, même si la sortie est en CSS natif.
4. Parité cross-navigateur dès maintenant Le support Firefox est arrivé en version 110 (derrière un flag) et débarque dans la version stable fin 2024, mais si vos analytics montrent plus de 15 % d'utilisateurs Firefox et que vous développez sans amélioration progressive — vous avez encore besoin d'une stratégie de repli.
Étape par étape : reconstruire 3 effets de défilement classiques en CSS pur
Effet 1 : Section sticky avec opacité scrubée
L'effet « le texte se révèle au défilement », anciennement un pilier de ScrollTrigger :
.sticky-section {
position: sticky;
top: 0;
height: 100vh;
}
@keyframes reveal-text {
from { opacity: 0; letter-spacing: 0.5em; }
to { opacity: 1; letter-spacing: normal; }
}
.sticky-section h2 {
animation: reveal-text linear both;
animation-timeline: view();
animation-range: entry 20% entry 80%;
}
Effet 2 : Galerie à défilement horizontal
Le défilement horizontal qui nécessitait autrefois 40 lignes de JS :
.gallery-track {
display: flex;
width: 400vw;
scroll-timeline-name: --gallery;
}
.gallery-item {
animation: slide-in linear both;
animation-timeline: --gallery;
}
Combinez avec scroll-snap-type pour ce rendu soigné et intentionnel qui nécessitait autrefois fullPage.js.
Effet 3 : Image héro en parallaxe
@keyframes parallax-drift {
from { transform: translateY(-15%); }
to { transform: translateY(15%); }
}
.hero-image {
animation: parallax-drift linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
Trente secondes de CSS. Aucune bibliothèque. Aucun événement de défilement. Fonctionne à 120 fps sur un écran ProMotion.
Amélioration progressive : ne laissez pas Firefox de côté
Le schéma est propre. Encapsulez les déclarations de l'API native dans une vérification @supports et laissez GSAP (chargé conditionnellement) gérer les navigateurs non compatibles :
/* Base : aucune animation */
.card { opacity: 0; transform: translateY(30px); transition: opacity 0.4s, transform 0.4s; }
/* Amélioration progressive */
@supports (animation-timeline: scroll()) {
.card {
animation: fade-up linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
opacity: 1; /* Réinitialisation : l'animation CSS gère cela */
transform: none;
}
}
Dans votre bundle JS, utilisez la détection de fonctionnalités pour importer conditionnellement votre bibliothèque de repli :
if (!CSS.supports('animation-timeline', 'scroll()')) {
import('./scroll-fallback.js').then(({ initFallbacks }) => initFallbacks());
}
Cela signifie que la majorité de vos utilisateurs n'ont aucune surcharge JS pour les effets de défilement, et que la minorité bénéficie d'un repli élégant.
Ce que cela implique pour la stack technique de votre prochain projet
Voici le cadre honnête pour la prochaine décision de votre projet :
- Site marketing, portfolio, vitrine d'agence ? Optez par défaut pour les animations CSS natives au défilement. N'ajoutez GSAP que pour les effets spécifiques qui le nécessitent.
- Application React/Next.js avec des animations interactives complexes ? Framer Motion conserve toute sa légitimité. Mais isolez les effets d'apparition pilotés par le défilement en CSS — n'importez pas une bibliothèque juste pour des fades.
- Travail créatif intensif en WebGL/Canvas ? Conservez votre couche d'animation JS. Envisagez Lenis pour l'entrée du défilement, et utilisez CSS là où les deux mondes n'ont pas besoin de communiquer.
L'ère où npm install était la réponse par défaut aux animations de défilement touche à sa fin — non pas parce que les bibliothèques d'animation JavaScript ne sont pas excellentes, mais parce que la plateforme les a rattrapées. Des studios primés comme Active Theory et Resn publient déjà des travaux qui s'appuient sur l'API native pour ses performances et sa composabilité.
Les développeurs qui créeront les expériences de défilement les plus rapides et les plus impressionnantes en 2025 ne seront pas ceux qui connaissent GSAP le mieux. Ce seront ceux qui savent exactement quand ne pas l'utiliser.
Commencez à auditer vos projets actuels. Choisissez un effet de défilement qui passe par une bibliothèque JS et reconstruisez-le en CSS pur cette semaine. L'onglet performances ne mentira pas.
