Layout Shift causado por transiciones CSS

Aprenda a encontrar y eliminar las transiciones CSS que crean layout shifts

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-27

Por qué las transiciones CSS causan layout shifts

Los Cumulative Layout Shifts causados por transiciones CSS a menudo ocurren temprano durante la fase de carga de la página. Estos layout shifts no ocurren de manera consistente, lo que los hace difíciles de depurar.

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

El problema de transition: all

La causa más común de los layout shifts relacionados con transiciones es transition: all. Según el capítulo de CSS del Web Almanac 2022, el 53% de las páginas utilizan transition: all. Eso significa que se anima cada cambio de propiedad, incluyendo las que afectan el diseño como width, height, margin y padding.

Una transición CSS tiene una propiedad (property), duración (duration), función de tiempo (timing-function) y un retraso (delay). La forma abreviada se ve así:

/* property | duration | timing-function | delay */
transition: margin-right 4s ease-in-out 1s;

El problema comienza cuando los desarrolladores escriben transition: all .3s ease en lugar de especificar la propiedad exacta que desean animar. Durante la carga de la página, un elemento "above-the-fold" como un menú de navegación hace una transición desde su estado inicial (sin estilos) hasta su estado final (con estilos o incluso oculto). Si la propiedad de transición es all, esto incluye width, height e incluso visibility. El resultado: un layout shift que solo sucede durante la carga y es casi imposible de reproducir de manera consistente.

¿Qué propiedades CSS causan layout shift?

No todas las transiciones CSS causan layout shifts. Solo las propiedades que cambian la geometría o la posición de un elemento activan el paso de diseño del navegador. Según la investigación de Google, las páginas que animan propiedades CSS que inducen layout shifts tienen un 15% menos de probabilidades de pasar el CLS. Las páginas que animan margin o border-width tienen un CLS deficiente a casi el doble de la tasa normal.

Propiedades que causan layout shift: width, height, margin, padding, top, left, right, bottom, border-width, font-size, display

Propiedades seguras (solo composición, sin layout shift): transform, opacity, filter, clip-path

Propiedades seguras (solo pintura, sin layout shift): background-color, color, box-shadow, outline

La clave: transform y opacity son manejados completamente por el hilo del compositor del navegador. Nunca activan el recálculo de diseño, por lo que nunca cuentan para el CLS. Si necesita mover un elemento, use transform: translate() en lugar de top/left. Si necesita cambiar el tamaño visualmente, use transform: scale() en lugar de width/height.

Eche un vistazo al ejemplo a continuación. Esto demuestra un layout shift causado por transiciones CSS que ocurren durante la fase de carga de una página. Veo este patrón todo el tiempo y encontrar y solucionar este tipo de problemas puede ser difícil.

Encontrar y solucionar transiciones CSS

Para encontrar y solucionar todos los layout shifts causados por transiciones CSS, necesitamos ejecutar una prueba rápida. Primero, encontramos todas las transiciones CSS en la página. Luego verificamos si alguna de ellas cambia propiedades que afecten el diseño. Finalmente, medimos el impacto de desactivar o modificar esas transiciones para confirmar que estaban causando CLS.

Consejo de Core Web Vitals: Los Cumulative Layout Shifts que son causados por transiciones CSS a menudo ocurren temprano durante la fase de carga de la página. Estos layout shifts no suceden de manera consistente, lo que los hace difíciles de depurar. ¡Ralentizar su red emulando un dispositivo móvil y desactivando su caché facilitará encontrarlos!

Paso 1: Encontrar transiciones CSS

La búsqueda de transiciones CSS se puede hacer manualmente: inspeccione todas las hojas de estilo y busque la palabra 'transition'. Eso no debería tomar más de 10 minutos de trabajo, ¡pero hay una manera mejor! Simplemente pegue este fragmento en la consola y presione enter

(() => {

  let nodeTable = [];
  let nodeArray = [];

  // Get the name of the node
  function getName(node) {
    const name = node.nodeName;
    return node.nodeType === 1
      ? name.toLowerCase()
      : name.toUpperCase().replace(/^#/, '');
  }

  // Get the selector
  const getSelector = (node) => {
    let sel = '';

    try {
      while (node && node.nodeType !== 9) {
        const el = node;
        const part = el.id
          ? '#' + el.id
          : getName(el) +
          (el.classList &&
            el.classList.value &&
            el.classList.value.trim() &&
            el.classList.value.trim().length
            ? '.' + el.classList.value.trim().replace(/\s+/g, '.')
            : '');
        if (sel.length + part.length > (100) - 1) return sel || part;
        sel = sel ? part + '>' + sel : part;
        if (el.id) break;
        node = el.parentNode;
      }
    } catch (err) {
      // Do nothing...
    }
    return sel;
  };

  const getNodesWithTransition = (node) => {

    // Get the computed style
    let cs = window.getComputedStyle(node);
    let tp = cs['transition-property'];
    let td = cs['transition-duration'];

    // If there is a transition, add it to the table
    if (tp !== '' && tp !== 'none' && td != '0s') {
      nodeTable.push({ selector: getSelector(node), transition: cs['transition'] });
      nodeArray.push(node);
    }

    // Recursively call this function for each child node
    for (let i = 0; i < node.children.length; i++) {
      getNodesWithTransition(node.children[i]);
    }
  }

  // find all transitions
  getNodesWithTransition(document.body);

  // Display the results in the console
  console.log('%cReadable table of selectors and their transitions', 'color: red; font-weight: bold;');
  console.table(nodeTable);

  console.log('%cNodeList for you to inspect (harder to read but more info)', 'color: red; font-weight: bold;');
  console.log(nodeArray);


  // styles to temporarity override the transitions
  let selectors = nodeTable.map((item) => item.selector).join(', ');

  console.log('%cSpecific CSS to disable all transitions on this page', 'color: red; font-weight: bold;');
  console.log(`<style>${selectors}{transition-property: none !important;}</style>`);

  console.log('%cGlobal CSS to disable all transitions on this page (not suggested on production)', 'color: red; font-weight: bold;');
  console.log(`<style>*{transition-property: none !important;}</style>`);

})()

Le mostrará una tabla de todas las transiciones, los elementos en los que están trabajando y más detalles sobre las transiciones.

Para encontrar layout shifts, busque propiedades de transición como width, height, margin, padding, top, left, display y especialmente all (ya que all incluye todas las propiedades de transición válidas).

Paso 2: Modificar las transiciones CSS

El fragmento de JavaScript anterior mostrará todas las transiciones, así como código de ejemplo sobre cómo desactivar esas transiciones. Para fines de pruebas rápidas, le sugiero que tome el camino fácil y desactive todas las transiciones con una simple línea de código CSS

<style>*{transition-property: none !important;}</style>

Por supuesto, para entornos en vivo se requiere un poco más de sutileza. Cuidadosamente elimine solo las propiedades de transición innecesarias por selector. Por ejemplo, cambie #button{transition: all .2s} a #button{transition: background-color .2s}

Paso 3: Medir el cambio en layout shift

El siguiente y último paso es medir el impacto. Puede usar mi extensión de Chrome Core Web Vitals Visualizer o una herramienta RUM como CoreDash para medir el impacto en la vida real de estos cambios de código. En los datos de monitoreo de CoreDash, los sitios que reemplazaron transition: all con propiedades específicas vieron su CLS mejorar un 40% en promedio.

Mejores prácticas para transiciones

  1. Especifique siempre la propiedad exacta: Nunca use transition: all. En su lugar, escriba transition: background-color .2s ease. Esto evita layout shifts accidentales de propiedades que no tenía la intención de animar.
  2. Use transform y opacity para animaciones: Estas dos propiedades son manejadas por el compositor y nunca activan el diseño. Use transform: translate() para mover elementos, transform: scale() para cambiar su tamaño visualmente y opacity para desvanecerlos. Esto es lo que Google recomienda para animaciones de alto rendimiento.
  3. Establezca dimensiones explícitas en los elementos animados: Si un elemento necesita hacer una transición, asegúrese de que tenga un width y height explícitos (o un aspecto ratio) antes y después de la transición. Esto evita que el contenido circundante se desplace. Para obtener más información sobre esto, consulte cómo solucionar layout shifts causados por imágenes de tamaño automático.
  4. Tenga cuidado con will-change: La propiedad CSS will-change le dice al navegador que se prepare para una animación promoviendo el elemento a su propia capa de composición. Pero viene con compensaciones: crea un nuevo contexto de apilamiento, aumenta el uso de memoria de la GPU y establece un bloque de contención para los descendientes posicionados. Aplíquelo dinámicamente con JavaScript justo antes de que comience la animación y elimínelo cuando termine. No deje will-change en su hoja de estilos de forma permanente.

A pesar de todo esto, el Web Almanac 2025 informa que el 40% de las páginas móviles todavía ejecutan animaciones no compuestas. La buena noticia: el CLS es el Core Web Vital con la tasa de aprobación más alta, un 81% en dispositivos móviles. La mala noticia: si su sitio se encuentra en el 19% que falla, las transiciones CSS podrían ser la causa que aún no ha verificado.

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.

Your Lighthouse score is not the full picture.

Your real users are on Android phones over 4G. I analyze what they actually experience.

Analyze field data
Layout Shift causado por transiciones CSSCore Web Vitals Layout Shift causado por transiciones CSS