Cómo las animaciones disparadas por el scroll provocan CLS

Tu header que se oculta al hacer scroll puede estar fallando en el CLS sin que te des cuenta. Te explico por qué y cómo solucionarlo.

Arjen Karel
Actualizado el · 6 min de lectura
Compartir

Cada vez que me piden auditar el Cumulative Layout Shift, siempre empiezo por los cambios durante la fase de carga. Reviso imágenes sin dimensiones, anuncios inyectados y sustituciones de fuentes (FOUT).

Pero no todo el CLS ocurre durante la carga de la página. A veces lo provocan interacciones como el scroll. Son difíciles de detectar porque Lighthouse no hace scroll y hay que saber exactamente dónde mirar.

Última revisión por Arjen Karel en marzo de 2026

En una auditoría reciente añadí datos RUM de CoreDash y vi un patrón sospechoso. Un elemento #header parecía provocar muchos más cambios de diseño de lo normal.

Resulta que lo que vemos aquí es un header fijo que se oculta cuando el usuario hace scroll hacia abajo y reaparece cuando hace scroll hacia arriba. Es un patrón que veo a menudo y que, si no se hace bien, provoca un cambio de diseño con cada cambio de dirección del scroll.

Por qué los cambios provocados por el scroll son diferentes

Normalmente el CLS no ocurre tras una interacción. El CLS mide cambios de diseño inesperados, y tras una interacción es de esperar que el diseño cambie. Por eso la métrica CLS tiene un periodo de gracia automático de 500 ms. Los cambios de diseño que ocurren dentro de los 500 milisegundos posteriores a una interacción del usuario se excluyen de la puntuación de CLS. El navegador gestiona esto activando y comprobando el flag hadRecentInput en estos cambios.

El problema es que esto solo se aplica a eventos discretos como clics, toques y pulsaciones de teclas. Según la especificación de Layout Instability, el scroll explícitamente no es una entrada excluyente. Tampoco lo son los arrastres ni los gestos de pellizcar para hacer zoom. Solo los eventos discretos reciben el periodo de gracia de 500 ms.

Esto significa que cada cambio de diseño provocado por un evento de scroll cuenta para la puntuación de CLS.

Según el Web Almanac de 2025, el 40% de las páginas móviles todavía utiliza animaciones no compuestas. Son animaciones en propiedades como top, height o margin que disparan el recálculo del layout en cada frame. Si alguna de ellas se ejecuta durante el scroll, cada frame cuenta para el CLS.

El problema del header que se oculta al hacer scroll

Empecemos por el problema que encontré hoy: el header que se oculta al hacer scroll. Un header fijo usa JavaScript para alternar la visibilidad al hacer scroll. La implementación tiene este aspecto:

/* CSS */
#header {
  position: fixed;
  top: 0;
  transition: top 0.3s ease;
}

/* JavaScript */
window.addEventListener('scroll', () => {
  if (scrollingDown) {
    header.style.top = '-80px';  // ocultar
  } else {
    header.style.top = '0px';    // mostrar
  }
});

El cambio de diseño lo provoca la combinación de transition: top 0.3s ease y la modificación de header.style.top en JavaScript.

El motivo es este: top es una propiedad de layout. Cuando el navegador procesa un cambio en top, ejecuta el pipeline de renderizado completo: Style, Layout, Paint, Composite. En cada frame de la animación durante esa transición, el navegador recalcula el layout. Cada recálculo genera una nueva entrada de layout shift.

Con cada cambio de dirección del scroll, el contador de CLS sigue subiendo mientras el usuario interactúa con la página.

Preparé una demo que reproduce exactamente este comportamiento a partir de un sitio real que estaba auditando y grabé un vídeo. El CLS total superó el 0,1 tras hacer un poco de scroll. Es verdad que este nivel de scroll no suele darse en la vida real, pero el mecanismo no cambia. Y he visto cómo este cambio de diseño empuja a páginas web al límite y hace que fallen en el CLS total.

¿Qué propiedades CSS pueden provocar cambios de diseño al animarse?

Para que una animación provoque CLS tiene que disparar el Layout. El pipeline de renderizado del navegador tiene tres etapas principales tras el cálculo de estilos: Layout, Paint y Composite. Las propiedades que solo afectan a la fase de Composite nunca provocan cambios de diseño. Para saber más sobre cómo las transiciones CSS provocan cambios de diseño, consulta el artículo complementario.

Propiedades que activan el layout (y provocan CLS al animarse):

top, left, right, bottom, width, height, margin, padding, border-width, font-size

Todas estas propiedades cambian la geometría o la posición de un elemento. El navegador debe recalcular la posición del elemento (y posiblemente la de otros también).

Propiedades que omiten el layout por completo (y son seguras para animar):

transform y opacity se ejecutan en el hilo del compositor. Omiten tanto el layout como el paint. El navegador las procesa en la GPU (por eso estas animaciones funcionan incluso cuando el main thread está ocupado). Al no disparar nunca el recálculo del layout, jamás generan entradas de layout shift.

Sustituye las propiedades de layout por transforms

El fix para el header que se oculta al hacer scroll es sencillo: sustituye la animación de top por transform: translateY().

/* CSS - ANTES (provoca CLS) */
#header {
  position: fixed;
  top: 0;
  transition: top 0.3s ease;
}

/* CSS - DESPUÉS (sin CLS) */
#header {
  position: fixed;
  top: 0;
  transition: transform 0.3s ease;
}

/* JavaScript - ANTES */
header.style.top = '-80px';

/* JavaScript - DESPUÉS */
header.style.transform = 'translateY(-80px)';

El resultado es el mismo: el header se desliza hacia arriba y desaparece. Pero transform: translateY() se ejecuta por completo en el compositor y nunca provoca un cambio de diseño.

El mismo principio se aplica a cualquier animación activada por scroll:

  • Paneles deslizantes: usa transform: translateX() en lugar de left o right
  • Elementos que se expanden: usa transform: scaleY() en lugar de height
  • Elementos con desvanecimiento: usa opacity en lugar de visibility combinada con cambios de altura

Otros patrones activados por scroll que provocan CLS

El header que se oculta al hacer scroll es algo que veo con frecuencia, pero hay más casos. Cualquier JavaScript que modifique una propiedad de layout en respuesta a eventos de scroll generará un cambio de diseño. Por ejemplo:

Efectos parallax mediante top o margin-top. Sustitúyelos por transform: translateY() o utiliza el enfoque con perspective y translateZ de CSS para un parallax con CSS puro.

Navegación sticky que cambia de altura al hacer scroll. Una barra de navegación que se reduce de 80px a 50px cuando el usuario supera cierto umbral de scroll. Si el cambio de altura se anima con transition: height, sustitúyelo por transform: scaleY().

Barras de progreso vinculadas al scroll mediante width. Un indicador de progreso de lectura que crece de izquierda a derecha animando width. Usa transform: scaleX() con transform-origin: left en su lugar.

Alternativa moderna: CSS Scroll-Driven Animations. La API animation-timeline: scroll() permite controlar animaciones a partir del avance del scroll sin nada de JavaScript. Como estas animaciones usan transform y opacity por defecto y se ejecutan en el compositor, nunca provocan cambios de diseño. La compatibilidad incluye Chrome 115+ y Edge 115+, mientras que Safari todavía está en desarrollo. Para efectos vinculados al scroll como parallax, barras de progreso y animaciones de aparición, esta es la solución más limpia. Si quieres prescindir por completo del scroll con JavaScript, la API de scroll-driven animations es el camino a seguir.

Por qué Lighthouse no detecta esto

Lighthouse ejecuta una carga de página simulada. No hace scroll en la página. No interactúa con ella tras la carga. Esto significa que los cambios de diseño provocados por el scroll son completamente invisibles en lab data.

La única forma de detectar estos cambios es mediante Real User Monitoring. Field data captura todo el ciclo de vida de la página, incluido cada cambio de diseño que ocurre mientras el usuario hace scroll, clics y navega. Si la puntuación de CLS en CrUX es peor que en Lighthouse, las animaciones activadas por scroll son una de las primeras cosas a investigar. CoreDash monitoriza el CLS de visitantes reales, incluido cada cambio provocado por el scroll, para que puedas ver con exactitud qué elementos son los responsables.

Cómo encontrar cambios de diseño provocados por el scroll

Abre Chrome DevTools. Ve al panel Performance. Inicia la grabación (el icono del círculo) y haz scroll hacia arriba y hacia abajo varias veces. Detén la grabación. Busca los rombos morados en la pista de Layout Shifts. Si ves una acumulación de cambios que coincide con la actividad de scroll, tienes un problema de CLS provocado por scroll. También puedes usar la extensión de Chrome Core Web Vitals Visualizer para ver el CLS en tiempo real mientras haces scroll.

Regla general

Si se mueve al hacer scroll, anímalo con transform. Si se desvanece al hacer scroll, anímalo con opacity. Estas propiedades son exclusivas del compositor.

En JavaScript: revisa los manejadores del evento scroll, los callbacks de IntersectionObserver y cualquier código JavaScript que cambie estilos según la posición del scroll. Si utiliza top, left, margin, width, height o padding, sustitúyelo por el transform equivalente.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

Arjen Karel

¿Necesitas una web más rápida?

Soy Arjen y llevo 17 años arreglando webs lentas. Si te encuentras con problemas que no puedes resolver por tu cuenta, envíame tu URL y hablamos.

Envíame tu URL
Arjen Karel
Arjen Karel, Consultor de rendimiento web desde 2009

¿Por qué no funciona nuestro fix?

A veces la CDN sigue sirviendo el HTML antiguo, o el fix se metió en la plantilla de desktop y el móvil usa otra. Envíame tu URL y el problema, y te respondo con varias opciones. Primero reviso tus field data, luego dedicamos una hora a tu web, y en menos de un día tienes mis notas por escrito. 350 € sin IVA.

Envíame tu URL

¿No tienes desarrollador para hacer el fix? Hago el trabajo yo mismo, en una copia de tu web.