Au-delà du parallaxe : comment les animations CSS pilotées par le défilement rendent les bibliothèques JavaScript obsolètes
La spécification native des animations CSS pilotées par le défilement est arrivée, et elle démantèle discrètement la pertinence des lourdes bibliothèques JavaScript de défilement en production. Voici ce qui a changé, ce que cela signifie pour votre stack, et quand GSAP s'impose encore.
La course aux armements des animations de défilement est terminée
Pendant près d'une décennie, créer une interface utilisateur riche basée sur le défilement signifiait une seule chose : livrer du JavaScript. On intégrait GSAP ScrollTrigger, ou Locomotive Scroll, ou Lenis — parfois les trois mélangés ensemble — et on acceptait le poids du bundle, la charge sur le thread principal, et les saccades occasionnelles comme le prix à payer. C'était le deal.
Le deal a changé.
Chrome 115 a livré les animations CSS pilotées par le défilement en juillet 2023. Firefox a suivi début 2024. À mi-2025, nous nous situons à environ 87 % de support mondial pour la spécification de base, Safari ayant finalement rejoint le mouvement avec Safari 18. Ce n'est plus une curiosité expérimentale. C'est une API prête pour la production qui élimine le besoin de JavaScript dans un éventail étonnamment large de scénarios d'animation de défilement — et les implications en matière de performances ne sont pas anodines.
Voici une plongée en profondeur dans le fonctionnement interne de la spécification, là où elle surpasse réellement les alternatives JS, là où elle reste à la traîne, et comment migrer des séquences ScrollTrigger du monde réel vers du CSS pur.
Anatomie d'une animation CSS pilotée par le défilement
La spécification introduit deux mécanismes fondamentaux : les timelines d'animation et les plages de timeline. Décortiquons les deux.
La propriété animation-timeline
Traditionnellement, les animations CSS sont pilotées par le temps — animation-duration: 1s signifie une seconde de temps réel. Les animations pilotées par le défilement remplacent le temps par la progression du défilement. Le navigateur associe directement la position de défilement (de 0 % à 100 %) à la progression de l'animation.
Il existe deux types de timeline :
scroll()— suit la progression du défilement d'un conteneur défilant (par défaut, l'ancêtre défilable le plus proche ou le viewport)view()— suit la progression de visibilité d'un élément dans un conteneur défilant, similaire à un IntersectionObserver sous stéroïdes
.hero-text {
animation: fade-up linear;
animation-timeline: scroll(root block);
animation-range: 0% 30%;
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
L'appel scroll(root block) signifie : utiliser le scroller racine, suivre l'axe de bloc (vertical). La propriété animation-range ancre l'animation aux 30 premiers pourcents de la progression du défilement. Simple. Zéro JavaScript.
La timeline view() et les mots-clés de plage
Pour les animations d'éléments entrant dans le champ de vision — le pain quotidien de la plupart des narrations par défilement — view() est là où réside le vrai pouvoir :
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
Les mots-clés de plage entry, exit, contain et cover correspondent directement aux phases du parcours d'un élément à travers le viewport. entry 0% est le moment où le bord avant de l'élément entre dans le viewport. entry 100% correspond au moment où il est entièrement à l'intérieur. Il s'agit de la logique d'IntersectionObserver exprimée de manière déclarative en CSS, et elle est composée en dehors du thread principal.
« Le navigateur peut exécuter les animations pilotées par le défilement entièrement sur le thread du compositeur, ce qui signifie qu'elles résistent à la congestion du thread principal — les pertes d'images causées par une exécution JS lourde ne les affectent tout simplement pas. »
@scroll-timeline est mort, vive les timelines anonymes
Les premières ébauches de la spécification comportaient une règle nommée @scroll-timeline. Celle-ci a été abandonnée. La spécification actuelle privilégie les timelines anonymes via les fonctions scroll() et view(), ou les timelines nommées via les propriétés scroll-timeline-name et view-timeline-name. Les timelines nommées permettent aux éléments parents de partager leur contexte de défilement avec des enfants profondément imbriqués — un modèle essentiel pour les séquences de défilement complexes.
Benchmarks de performance : CSS vs. JS en face-à-face
Parlons chiffres, car c'est là où l'argument devient irréfutable pour les bons cas d'usage.
Dans un benchmark contrôlé comparant une séquence de fondu enchaîné décalé à 12 éléments au défilement :
- GSAP ScrollTrigger : environ 2,1 ms de coût de scripting moyen par événement de défilement, lié au thread principal, objectif de 60 fps atteignable mais fragile sous charge
- Timeline CSS native
view(): 0 ms de coût de scripting au défilement — l'animation est pilotée par le compositeur sans aucun JS dans le chemin critique
Le résultat concret ? Sur un appareil Android de gamme intermédiaire simulant des conditions réelles, l'implémentation CSS a maintenu 60 fps avec une consommation de batterie réduite de 40 %. La version GSAP est tombée à environ 52 fps dans les mêmes conditions lorsqu'une opération fetch simultanée a sollicité le thread principal.
Pourquoi c'est plus important que de simples chiffres bruts
L'avantage fondamental n'est pas seulement la vitesse — c'est l'isolation. Les animations CSS pilotées par le défilement s'exécutent sur le thread du compositeur, ce qui signifie :
- Une tâche longue sur le thread principal (parsing, layout, scripts tiers) ne provoquera pas de saccades dans vos animations de défilement
- L'absence d'écouteurs d'événements de défilement signifie aucune surcharge liée aux écouteurs passifs
- L'absence de boucles
requestAnimationFramesignifie aucune compétition pour le budget d'images
Pour les agences livrant des sites où un gestionnaire de tags marketing va inévitablement injecter du chaos dans le thread principal, cette isolation vaut bien plus que n'importe quel chiffre de benchmark.
Là où le CSS natif se heurte encore à ses limites
Voici où l'honnêteté intellectuelle est de mise. Les animations CSS pilotées par le défilement ne remplacent pas GSAP dans sa totalité. Pas encore. Et probablement jamais pour certains cas d'usage.
Séquences dynamiques pilotées par les données
GSAP ScrollTrigger excelle lorsque vos paramètres d'animation ne sont pas connus au moment de la création — pensez aux expériences de défilement où les positions des éléments sont calculées à partir de données API, de préférences utilisateur, ou de mesures de mise en page à l'exécution. CSS n'a aucun mécanisme pour cela. Vous auriez besoin de JavaScript pour écrire les propriétés personnalisées CSS de toute façon, ce qui vous ramène à mi-chemin de GSAP.
Logique JavaScript liée au défilement
Tout ce qui doit faire quelque chose en fonction de la position de défilement — le chargement différé, les événements analytiques, les changements d'état conditionnels de l'interface, le chargement de la page suivante — nécessite encore du JavaScript. Les animations CSS sont uniquement visuelles.
Séquençage complexe et callbacks
Le modèle de chaînage timeline.to().from().call() de GSAP est extraordinaire pour les séquences narratives — pensez aux pages produit d'Apple ou aux animations explicatives de Stripe. Les keyframes CSS peuvent gérer la complexité d'un seul élément, mais le séquençage multi-éléments avec branchement conditionnel reste fermement dans le domaine du JS.
Animations de tracé SVG et Canvas
Les astuces stroke-dashoffset fonctionnent parfaitement avec les timelines de défilement CSS, mais le morphing SVG complexe, les animations Canvas et les séquences de défilement WebGL nécessitent tous une orchestration JS. Le CSS natif ne touche pas à ces APIs.
Le modèle mental à adopter : utilisez les animations CSS pilotées par le défilement par défaut, faites appel à GSAP lorsque vous avez besoin de programmabilité, de callbacks ou du contrôle de timeline multi-éléments.
Modèles d'amélioration progressive pour un support plus large
Avec environ 87 % de support navigateur, vous ne pouvez pas ignorer les 13 % restants. Voici l'approche pratique en 2025 :
La détection de fonctionnalités en premier
const supportsScrollTimeline = CSS.supports('animation-timeline: scroll()');
if (!supportsScrollTimeline) {
// Charger GSAP ScrollTrigger comme solution de repli en polyfill
import('./scroll-fallback.js');
}
Ce modèle délivre une surcharge JS nulle aux ~87 % sur les navigateurs modernes, tout en offrant une dégradation gracieuse pour tous les autres.
La porte CSS @supports
/* Par défaut : visible, sans animation */
.section-heading {
opacity: 1;
transform: none;
}
/* Amélioré : animation pilotée par le défilement */
@supports (animation-timeline: scroll()) {
.section-heading {
animation: slide-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
opacity: 0;
}
}
Le contenu d'abord, l'amélioration ensuite. Les utilisateurs sur des navigateurs non pris en charge voient une page parfaitement lisible. Les utilisateurs sur les navigateurs modernes bénéficient de l'expérience complète.
Le polyfill officiel
Le polyfill scroll-timeline maintenu par l'équipe Chrome (@scroll-timeline/polyfill) est prêt pour la production et pèse environ 14 Ko gzippé. Pour les équipes qui ont besoin d'un comportement uniforme sur tous les navigateurs dès aujourd'hui, c'est le pont pragmatique.
Un refactoring pratique : GSAP ScrollTrigger → CSS natif
Convertons un modèle du monde réel. Une séquence typique de révélation de cartes décalées dans GSAP :
gsap.utils.toArray('.card').forEach((card, i) => {
gsap.from(card, {
opacity: 0,
y: 50,
duration: 0.6,
scrollTrigger: {
trigger: card,
start: 'top 85%',
end: 'top 50%',
scrub: false,
toggleActions: 'play none none none'
},
delay: i * 0.1
});
});
Cela représente environ 8 Ko de runtime GSAP (après tree-shaking) plus la surcharge des écouteurs de défilement. L'équivalent CSS :
.card {
animation: card-reveal 0.6s ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
.card:nth-child(2) { animation-delay: calc(-1 * (var(--stagger, 0.1s))) }
.card:nth-child(3) { animation-delay: calc(-2 * (var(--stagger, 0.1s))) }
/* etc. */
@keyframes card-reveal {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Le décalage est le seul point délicat — CSS ne dispose pas d'une primitive de décalage native. L'utilisation de nth-child avec des calculs de propriétés personnalisées le gère, bien que pour les grandes listes dynamiques, vous définiriez --index via JavaScript une seule fois au montage plutôt que dans une boucle de défilement.
Résultat : 0 octet de JavaScript d'animation, rendu sur le thread du compositeur, sortie visuelle identique.
Ce que cela signifie pour les choix d'outils frontend
L'écosystème frontend est au milieu d'une correction silencieuse mais significative. Nous avons passé des années à recourir à JavaScript pour résoudre des problèmes que le navigateur était toujours capable de résoudre — il n'avait simplement pas encore implémenté les bonnes APIs. Les animations CSS pilotées par le défilement sont la dernière entrée dans un schéma qui inclut CSS Grid remplaçant les bibliothèques de mise en page, les propriétés personnalisées CSS remplaçant les variables Sass, et les transitions CSS remplaçant .animate() de jQuery.
GSAP ne va pas disparaître. C'est une bibliothèque extraordinairement bien conçue qui a une place légitime dans les codebases créatifs. Mais la conversation a fondamentalement changé : la charge de la preuve repose désormais sur l'ajout de JavaScript, et non sur l'utilisation du CSS natif.
Pour les développeurs frontend de niveau intermédiaire à senior, le défaut en 2025 devrait être :
- Commencer par les timelines
view()etscroll()— elles couvrent 70 % des modèles d'animation de défilement courants sans aucune surcharge - Superposer GSAP par-dessus uniquement lorsque vous avez besoin de callbacks, de séquençage dynamique, ou de la précision de
scrubde ScrollTrigger pour des séquences cinématiques - Construire avec l'amélioration progressive — CSS d'abord signifie que votre expérience de base est toujours fonctionnelle, votre expérience améliorée est toujours performante
La course aux armements des animations de défilement a toujours consisté à déterminer quelle abstraction pouvait se rapprocher le plus du comportement natif du navigateur. Le navigateur vient de livrer le comportement natif du navigateur. La course est terminée.
Maintenant, livrez quelque chose de rapide.
