Pendant des années, animer des éléments en fonction du scroll nécessitait du JavaScript : un listener sur l'événement scroll, un calcul de position, et une mise à jour manuelle des propriétés CSS. C'était verbeux, fragile et mauvais pour les performances. En 2026, cette époque est révolue.
Les spécifications CSS Scroll-Driven Animations sont désormais supportées par tous les navigateurs majeurs. Chrome, Firefox, Safari et Edge implémentent tous les propriétés scroll-timeline et view-timeline. Il est temps de les adopter pleinement.
Le principe fondamental
L'idée derrière les animations pilotées par le scroll est élégante : au lieu d'animer un élément en fonction du temps (comme avec @keyframes classiques), on l'anime en fonction de la progression du scroll. L'animation avance quand l'utilisateur scrolle vers le bas, recule quand il scrolle vers le haut.
Deux types de timelines existent :
- Scroll Timeline : l'animation est liée à la progression du scroll dans un conteneur (souvent le viewport). De 0% (en haut) à 100% (en bas).
- View Timeline : l'animation est liée à la visibilité d'un élément dans le viewport. De 0% (l'élément entre dans le viewport) à 100% (il en sort).
Scroll Timeline : les bases
Commençons par un exemple simple. Imaginons une barre de progression qui se remplit au fur et à mesure que l'utilisateur scrolle la page :
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg, #c084fc, #f472b6);
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll();
}
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
C'est tout. Pas de JavaScript. La propriété animation-timeline: scroll() remplace la timeline temporelle par défaut par une timeline de scroll. L'animation avance naturellement avec le défilement de la page.
La fonction scroll() accepte deux paramètres optionnels :
/* Syntaxe complète */
animation-timeline: scroll(<scroller> <axis>);
/* Exemples */
animation-timeline: scroll(); /* root, block */
animation-timeline: scroll(root); /* root scroller, block axis */
animation-timeline: scroll(nearest); /* nearest scrollable ancestor */
animation-timeline: scroll(root inline); /* root scroller, inline axis */
View Timeline : animer à l'entrée dans le viewport
La View Timeline est probablement ce que vous utiliserez le plus souvent. Elle permet d'animer un élément au moment où il entre dans le viewport, exactement comme le faisait IntersectionObserver avec du JavaScript.
.card {
animation: fade-in-up linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
La propriété animation-range est cruciale ici. Elle définit la portion de la timeline sur laquelle l'animation se déroule. entry 0% correspond au moment où l'élément commence à entrer dans le viewport, entry 100% correspond au moment où il est complètement visible.
Les ranges disponibles
La spécification définit plusieurs ranges nommées :
entry: de l'entrée du bord avant jusqu'à ce que l'élément soit entièrement visible.exit: de la sortie du bord avant jusqu'à ce que l'élément soit entièrement hors du viewport.contain: la période pendant laquelle l'élément est entièrement contenu dans le viewport.cover: de la première intersection jusqu'à la dernière.
/* Fade-in uniquement à l'entrée */
animation-range: entry;
/* Animation pendant toute la visibilité */
animation-range: cover;
/* Animation entre 25% de l'entrée et 75% de la sortie */
animation-range: entry 25% exit 75%;
Timelines nommées
Parfois, vous voulez que l'animation d'un élément soit liée au scroll d'un autre conteneur. C'est là que les timelines nommées interviennent :
.scroll-container {
overflow-y: auto;
scroll-timeline-name: --my-scroller;
scroll-timeline-axis: block;
}
.animated-element {
animation: slide-in linear both;
animation-timeline: --my-scroller;
}
@keyframes slide-in {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
On peut aussi utiliser view-timeline-name pour créer une view timeline nommée, ce qui est utile quand l'élément animé et l'élément observé sont différents.
Cas d'usage concrets
Parallax sans JavaScript
L'effet parallax, longtemps gourmand en JavaScript, devient trivial :
.parallax-bg {
animation: parallax linear;
animation-timeline: scroll();
}
@keyframes parallax {
from { transform: translateY(0); }
to { transform: translateY(-30%); }
}
Révéler une galerie d'images
.gallery-item {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 10% entry 90%;
}
@keyframes reveal {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
filter: blur(4px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
filter: blur(0);
}
}
Header qui se compacte au scroll
.site-header {
animation: compact-header linear both;
animation-timeline: scroll();
animation-range: 0px 200px;
}
@keyframes compact-header {
from {
padding-block: 24px;
background: transparent;
}
to {
padding-block: 8px;
background: rgba(0, 0, 0, 0.9);
backdrop-filter: blur(20px);
}
}
Performances : pourquoi c'est mieux
Les animations pilotées par le scroll en CSS sont fondamentalement plus performantes que leurs équivalents JavaScript pour plusieurs raisons :
- Pas de listener scroll : les listeners
scrollse déclenchent à chaque frame, bloquant le thread principal. Les animations CSS sont gérées par le compositor. - Pas de reflow : le navigateur sait à l'avance quelles propriétés vont changer et peut optimiser en conséquence.
- 60 fps natif : les animations CSS tournent naturellement à la fréquence de rafraîchissement de l'écran.
- Respect du prefers-reduced-motion : vous pouvez facilement désactiver ces animations dans une media query.
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0s !important;
animation-timeline: none !important;
}
}
Compatibilité navigateur en 2026
Au moment d'écrire ces lignes (mars 2026), le support est excellent :
- Chrome / Edge : support complet depuis la version 115 (juillet 2023).
- Firefox : support complet depuis la version 132 (fin 2024).
- Safari : support complet depuis la version 18.2 (début 2025).
Cela signifie que plus de 95% des utilisateurs ont accès à cette fonctionnalité. Pour les navigateurs plus anciens, l'animation ne se déclenche simplement pas : l'élément reste dans son état final, ce qui constitue un fallback gracieux.
Conseils pratiques
Utilisez les animations au scroll avec modération. Un site où tout bouge en permanence fatigue l'utilisateur. Réservez-les aux moments où elles servent véritablement le contenu.
Quelques bonnes pratiques :
- Animez des propriétés composites :
transformetopacitysont les meilleures candidates. Évitez d'animerwidth,heightoumargin. - Testez sur mobile : le comportement du scroll sur mobile (avec le bounce, les barres d'adresse qui se cachent, etc.) peut affecter vos animations.
- Utilisez
animation-range: ne faites pas durer l'animation sur toute la timeline. Concentrez-la sur la portion pertinente. - Pensez accessibilité : respectez toujours
prefers-reduced-motion. - Combinez avec
will-change: si vous animez beaucoup d'éléments simultanément, indiquez au navigateur les propriétés qui vont changer.
Conclusion
Les animations CSS pilotées par le scroll représentent l'une des avancées les plus significatives du CSS ces dernières années. Elles éliminent des centaines de lignes de JavaScript, améliorent les performances et offrent une syntaxe déclarative élégante.
En 2026, il n'y a plus aucune raison d'utiliser un listener scroll pour des animations. Le CSS gère désormais cela nativement, de manière plus performante et plus maintenable. Il est temps de réécrire vos effets de scroll en pur CSS.