Barras de progreso de lectura, tarjetas que entran al aparecer, un titular que se desvanece al pasar de largo: todo eso ya puede ser CSS puro en todos los navegadores principales. Firefox 159 salió el 27 de octubre de 2026 con las animaciones ligadas al scroll activadas por defecto, y ese mismo día la funcionalidad pasó a ser Baseline. En la demo de arriba, haz scroll dentro del panel: la barra, el porcentaje, el anillo 3D y las tarjetas siguen tu scroll, y en la página no hay ni un solo listener de scroll.
Qué cambia
Una animación CSS normal corre sobre la línea de tiempo del documento: avanza del primer fotograma clave al último a medida que pasa el tiempo. Las animaciones ligadas al scroll cambian ese reloj por otro con la propiedad animation-timeline. Hay dos tipos de línea de tiempo:
- De progreso de scroll, con
scroll(): el progreso va del 0 % al 100 % según un contenedor se desplaza de arriba abajo. - De progreso de vista, con
view(): el progreso depende de cuánto ha recorrido un elemento (el sujeto) por la zona visible de su contenedor de scroll.
Sigues escribiendo los mismos @keyframes de siempre. Lo que cambia es quién los mueve. Y como es declarativo, no necesitas eventos scroll, requestAnimationFrame ni un IntersectionObserver para sincronizar nada.
Cómo funciona
La barra de progreso de arriba de la demo es el ejemplo clásico:
.progress {
transform-origin: 0 50%;
transform: scaleX(0);
animation: grow 1ms linear both;
animation-timeline: scroll(nearest block); /* después del shorthand */
}
@keyframes grow { to { transform: scaleX(1); } }
scroll() acepta dos argumentos opcionales: qué contenedor usar (nearest, el valor por defecto; root para el documento; o self) y qué eje (block por defecto, o inline, x, y). Hay dos detalles importantes en ese bloque:
- El orden. El shorthand
animationrestableceanimation-timelineaauto, pero no puede darle valor. MDN lo dice sin rodeos: declaraanimation-timelinedespués de cualquier shorthandanimation, o no tendrá efecto. - El
1ms. En una línea de tiempo de scroll la duración no controla nada, pero según MDN, Firefox necesita unaanimation-durationdistinta de cero para aplicar la animación. Por eso1mses la convención habitual.
Las tarjetas usan view(), y animation-range decide en qué tramo de su recorrido ocurre la animación:
.card {
animation: reveal 1ms linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes reveal {
from { opacity: 0; transform: translateY(60px) rotateX(-55deg) scale(.86); }
to { opacity: 1; transform: none; }
}
La especificación define rangos con nombre para una línea de tiempo de vista. cover es el recorrido completo, desde que el elemento empieza a entrar hasta que ha salido del todo. contain es el tramo en el que está entero dentro de la zona visible. entry y exit son los tramos en los que entra y sale. Los botones de arriba de la demo cambian entre entry, cover 0% cover 50% y contain. Con la misma posición de scroll, la misma tarjeta queda con una opacidad distinta, porque cada rango asigna esa posición a un punto distinto de la animación.
También puedes ponerle nombre a una línea de tiempo y usarla desde otro elemento. El panel declara scroll-timeline: --page block, y el anillo 3D y el contador usan animation-timeline: --page. El contador es un truquito: un entero registrado con @property, animado de 0 a 100 y pintado con counter(). El porcentaje que ves lo calcula el navegador mientras haces scroll.
Desde JavaScript tienes la misma línea de tiempo. Esta es la línea que alimenta el marcador de la demo:
const timeline = new ScrollTimeline({ source: viewport, axis: 'block' });
timeline.currentTime; // CSSUnitValue en porcentaje, p. ej. 42%
La especificación define currentTime como un CSSUnitValue en porcentaje, y como null cuando la línea de tiempo está inactiva.
Soporte y trampas
- Soporte: Chrome y Edge 115, Safari 26 (las notas de la versión dicen "Added support for Scroll-driven Animations") y Firefox 159. Según los datos de web-features, es Baseline "newly available" desde el 27 de octubre de 2026. Quien no haya actualizado todavía no lo verá, así que detéctalo: la demo comprueba
CSS.supports('animation-timeline: scroll()')y, si falla, muestra un aviso y usa un plan B en JavaScript con un listener de scroll y unIntersectionObserver. En CSS, lo equivalente es@supports (animation-timeline: scroll()). overflow: hiddencrea un contenedor de scroll. Esta me pilló montando la demo. La pista de la barra teníaoverflow: hiddenpara redondear las esquinas, y en Chromium la barra se quedaba a cero. La definición de MDN explica por qué:hiddenconvierte un elemento en contenedor de scroll, así quescroll(nearest)encontraba la pista (que no se desplaza) en lugar del panel. Cambiarlo aoverflow: cliplo arregló, porqueclipno crea un contenedor de scroll.- El shorthand no fija la línea de tiempo. En los datos de compatibilidad de MDN,
animation-timelinedentro deanimationes solo "reset-only" en Chromium y no está soportado en Firefox ni en Safari. Escríbelo siempre como declaración aparte. - El movimiento sigue necesitando un interruptor. MDN recuerda que hay que respetar
prefers-reduced-motion. En la demo, con movimiento reducido, el anillo y el titular dejan de moverse y las tarjetas solo aparecen con un fundido, sin rotación 3D. La barra de progreso se queda, porque no desplaza nada por la pantalla.
Qué vigilar
- El estado "widely available". Baseline "newly available" significa que todos los motores lo incluyen, no que todos los usuarios hayan actualizado. Mantén el plan B una temporada.
- Tus librerías de scroll. Si cargas una librería solo para barras de progreso o efectos de aparición al hacer scroll, merece la pena revisar cuánto de eso cubre ya CSS.
- La especificación sigue siendo un Editor's Draft. El documento del CSSWG está marcado como "revising" y aún tiene cuestiones abiertas, por ejemplo qué significa el 0 % en algunos contenedores de scroll. Lo básico que se ve aquí funciona hoy en los tres motores, pero quedan detalles por cerrar.
Fuentes
- animation-timeline – MDN (código fuente en GitHub)
- Líneas de tiempo de las animaciones ligadas al scroll – guía de MDN (código fuente en GitHub)
- Notas de Firefox 159 para desarrolladores – MDN (código fuente en GitHub)
- Notas de la versión de Safari 26 – Apple Developer
- Scroll-driven Animations, código fuente del Editor's Draft – CSS Working Group