Cómo corregir el CLS: qué hace que la página salte

Qué cuenta en el CLS, causas habituales (imágenes, anuncios, fuentes, animaciones) y cómo reservar espacio en HTML y CSS. Cómo hallar el elemento que se mueve.

Verificar mi sitio gratisVer los planes
En esta página 8 min de lectura
  1. Cómo se calcula el CLS
  2. Causas habituales y cómo corregirlas
  3. Cómo encontrar el elemento que se movió
  4. Caché de avance y retroceso (bfcache)
  5. Cómo revisar tu sitio

El CLS (Cumulative Layout Shift) mide cuánto cambia de lugar el contenido visible sin que el usuario lo haya pedido. Es bueno hasta 0,1 y malo por encima de 0,25, en el percentil 75 de las visitas (web.dev). Las causas más comunes son imágenes, anuncios e iframes sin tamaño definido, contenido insertado después de la carga, el cambio de fuente y animaciones que afectan al layout. Casi todas se resuelven reservando el espacio del elemento antes de que llegue.

Este texto sigue las guías de web.dev sobre CLS y optimización del CLS (actualizada en febrero de 2025). Para el contexto de las tres Core Web Vitals, consulta la guía de Core Web Vitals.

Cómo se calcula el CLS

Un desplazamiento de layout ocurre cuando un elemento visible cambia de posición entre dos fotogramas. Cada desplazamiento recibe una puntuación:

puntuación = fracción de impacto × fracción de distancia

  • La fracción de impacto es el área visible que ocupan los elementos que se movieron (sumando la posición antigua y la nueva) dividida entre el área de la pantalla.

  • La fracción de distancia es el mayor movimiento de uno de esos elementos, en horizontal o en vertical, dividido entre la mayor dimensión de la pantalla.

Los desplazamientos se agrupan en ventanas de sesión. Una ventana reúne desplazamientos separados por menos de 1 segundo entre sí y dura como máximo 5 segundos. El CLS de la página es la suma de la ventana con la puntuación más alta, y no la suma de todos los desplazamientos de la visita (web.dev). Por eso una página abierta durante mucho tiempo no acumula CLS sin límite.

Los desplazamientos esperados no cuentan

Si el usuario hace clic en un botón que abre un panel, que el contenido se mueva es el resultado esperado. La API marca con hadRecentInput los desplazamientos que ocurren hasta 500 ms después de una interacción discreta (toque, clic o tecla), y esos quedan fuera del CLS. Desplazarse, arrastrar y el gesto de pellizco no abren esa ventana (web.dev). Si la respuesta a un clic tarda más de 500 ms en cambiar el layout, como en un "cargar más" que espera a la red, el desplazamiento pasa a contar. En esos casos, reserva el espacio o muestra un indicador de carga en cuanto se hace clic.

Causas habituales y cómo corregirlas

Imágenes sin ancho ni alto

Sin dimensiones, el navegador no sabe cuánto espacio reservar y solo lo descubre cuando llega la imagen, empujando el texto hacia abajo. La corrección es declarar width y height en el HTML. Los navegadores actuales usan esos dos valores para calcular la proporción y reservar el espacio, incluso cuando el CSS hace que la imagen sea responsiva (web.dev):

<img src="/img/producto.jpg" width="640" height="360" alt="Descripción del producto">
img {
  max-width: 100%;
  height: auto;
}

El navegador aplica internamente aspect-ratio: auto 640 / 360. La palabra auto hace que la proporción real del archivo valga cuando se descarga. Los valores de width y height no tienen que ser el tamaño mostrado, solo tienen que tener la proporción correcta.

En imágenes responsivas, todas las opciones de srcset deben tener la misma proporción. Si cada tamaño de pantalla usa un recorte distinto, declara las dimensiones en cada <source> del <picture>:

<picture>
  <source media="(max-width: 799px)" srcset="/img/banner-480-recorte.jpg"
          width="480" height="400">
  <source media="(min-width: 800px)" srcset="/img/banner-800.jpg"
          width="800" height="400">
  <img src="/img/banner-800.jpg" width="800" height="400" alt="Descripción del banner">
</picture>

Para elementos que no tienen atributos de dimensión, como un contenedor de vídeo, la propiedad CSS aspect-ratio reserva el espacio. Funciona desde Chrome 88, Firefox 89 y Safari 15 (MDN browser-compat-data).

.video-container {
  aspect-ratio: 16 / 9;
  width: 100%;
}

Anuncios, embeds e iframes

Los bloques de anuncios, los vídeos incrustados y los widgets de terceros suelen llegar después del contenido y con un tamaño variable. web.dev recomienda (web.dev):

  • reservar el espacio con min-height, o con aspect-ratio cuando el contenido es responsivo;

  • tomar como base el tamaño de la creatividad más pequeña esperada, y usar media queries cuando el tamaño cambia entre móvil y escritorio;

  • mantener un placeholder cuando no se sirve ningún anuncio, en lugar de cerrar el espacio, porque quitar el espacio reservado también provoca desplazamiento.

/* Los valores son un ejemplo: usa el tamaño del anuncio más pequeño de cada posición */
.anuncio-superior {
  min-height: 100px;
}

@media (min-width: 768px) {
  .anuncio-superior {
    min-height: 90px;
  }
}

Contenido insertado después de la carga

Los avisos, las franjas de promoción, los formularios y las listas que aparecen con JavaScript empujan lo que está debajo si no tienen espacio reservado. Las opciones de web.dev:

  • reservar el espacio del elemento desde el HTML inicial;

  • colocar el contenido tardío más abajo en la página, donde el desplazamiento afecta a menos área visible;

  • en listas y feeds, cambiar el contenido dentro de un contenedor de tamaño fijo, o dejar que el usuario cargue más con un botón (el desplazamiento justo después del clic no cuenta);

  • cargar el contenido nuevo fuera de la pantalla y avisar de que hay novedades, en lugar de insertarlo arriba.

Fuentes web

Mientras la fuente web se descarga, el navegador muestra el texto invisible o con una fuente de reserva. Cuando la fuente web llega y tiene medidas distintas de la de reserva, las líneas cambian de ancho y de alto y el contenido se mueve. El comportamiento se controla con font-display (web.dev):

ValorTexto invisible hastaCambio a la fuente webEfecto en el CLS
block2 a 3 sen cualquier momentopuede desplazar en el cambio
swap0 msen cualquier momentopuede desplazar en el cambio
fallback100 mshasta 3 spuede desplazar en el cambio
optional100 msno cambia despuésno desplaza por cambio de fuente

Con optional, la fuente web solo se usa si está lista en el primer layout. Si la marca exige siempre la fuente web, usa swap y acerca la fuente de reserva a sus medidas. Eso se hace con una regla @font-face para la fuente local de reserva, ajustada con size-adjust y con las propiedades de métricas ascent-override, descent-override y line-gap-override (Chrome for Developers):

@font-face {
  font-family: "poppins-fallback";
  src: local("Arial");
  size-adjust: 60.85099821%;
  ascent-override: 164.3358416%;
  descent-override: 57.51754455%;
  line-gap-override: 16.43358416%;
}

body {
  font-family: "Poppins", "poppins-fallback", sans-serif;
}

Los valores de arriba son los del artículo de Chrome para Poppins sobre Arial; cada par de fuentes tiene los suyos, calculados a partir de las métricas de los archivos. Una reserva distinta, como Roboto en Android, necesita otro conjunto. En cuanto a compatibilidad, según los datos de MDN: size-adjust funciona desde Chrome 92, Firefox 92 y Safari 17; las tres propiedades de override existen en Chrome y Firefox, pero en Safari solo aparecen en la versión de pruebas (Technology Preview) (MDN browser-compat-data, consultado en octubre de 2026). En el Safari estable, por tanto, solo vale size-adjust.

web.dev también recomienda hacer preload de las fuentes críticas con <link rel="preload">, para que lleguen antes. El preload compite por el ancho de banda con otros recursos; úsalo solo para las fuentes de la primera pantalla. La misma elección de font-display afecta al LCP cuando el elemento más grande es texto (consulta cómo mejorar el LCP).

Animaciones que afectan al layout

Animar propiedades de posición y tamaño, como top, left, width y height, recalcula el layout y puede generar desplazamientos. Usa transform (translate, scale, rotate), que mueve el elemento sin tocar el flujo de la página (web.dev):

/* Evítalo */
.aviso { position: relative; transition: top 0.3s; }
.aviso.abierto { top: 0; }

/* Mejor así */
.aviso { transition: transform 0.3s; transform: translateY(-100%); }
.aviso.abierto { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .aviso { transition: none; }
}

Cómo encontrar el elemento que se movió

  1. En Chrome DevTools, la vista de métricas en directo del panel Performance muestra el CLS local y una lista de desplazamientos con el elemento, el momento y la puntuación. En una grabación, la pista Layout Shifts agrupa los desplazamientos y el insight "Layout shift culprits" señala las causas probables (web.dev).

  2. Lighthouse lista las imágenes sin width ni height y los elementos que se desplazaron durante la carga.

  3. Para datos de usuarios reales, la biblioteca web-vitals en su versión de atribución indica el elemento del mayor desplazamiento, cuándo ocurrió, cuánto puntuó y en qué fase de la carga estaba la página (GitHub GoogleChrome/web-vitals).

import {onCLS} from 'web-vitals/attribution';

onCLS(({value, attribution}) => {
  console.log('CLS', value);
  console.log('Elemento', attribution.largestShiftTarget);
  console.log('Cuándo (ms)', attribution.largestShiftTime);
  console.log('Puntuación', attribution.largestShiftValue);
  console.log('Estado de la página', attribution.loadState);
}, {reportAllChanges: true});

La opción reportAllChanges ayuda a depurar porque avisa con cada aumento del CLS; el README de la biblioteca no recomienda usarla en producción. La API que mide los desplazamientos (Layout Instability) solo existe en los navegadores Chromium: web-vitals indica compatibilidad de onCLS() solo en Chromium (GitHub GoogleChrome/web-vitals). Los datos de campo de CLS, en el RUM o en el CrUX, proceden de esos navegadores. La diferencia entre el dato de laboratorio y el de campo está en Lighthouse, CrUX y RUM.

Caché de avance y retroceso (bfcache)

Cuando el usuario vuelve a una página que estaba en la bfcache, se restaura desde la memoria tal como estaba, sin repetir los desplazamientos de la carga, y el CLS de esa visita empieza de cero (web.dev). Por eso conviene que las páginas puedan entrar en esa caché. Los impedimentos más comunes son los listeners de unload (cámbialos por pagehide), la cabecera Cache-Control: no-store en el HTML y las conexiones abiertas (IndexedDB, WebSocket, peticiones en curso) en el momento en que el usuario sale. En Chrome DevTools, en Application, la sección Back/forward cache tiene una prueba que muestra si la página se restauró y, si no, por qué.

Cómo revisar tu sitio

En WebVyze, el diagnóstico de CLS usa Google PageSpeed para mostrar la situación de la página, y el test de Core Web Vitals muestra el CLS del CrUX de los últimos 28 días, en móvil y escritorio, para hasta 10 URL. Las dos herramientas son gratuitas. En los planes de pago, un script de visitantes reales basado en la biblioteca web-vitals registra el CLS de cada visita con su causa. Para problemas de respuesta al clic, consulta cómo mejorar el INP.

Preguntas frecuentes

¿El CLS tiene unidad, como los segundos?
No. Es un número sin unidad que combina cuánta área visible se movió y la distancia del movimiento.
¿Desplazarse por la página cuenta como interacción para excluir el desplazamiento de layout?
No. La exclusión de 500 ms vale para toques, clics y teclas. Desplazarse, arrastrar y el gesto de pellizco no cuentan.
¿Los desplazamientos dentro de iframes entran en el CLS?
Entran en el CLS del CrUX, porque el usuario ve esos movimientos. Las API del navegador no pueden medirlos, así que la biblioteca web-vitals no los ve.
¿Por qué el CLS de Lighthouse es menor que el del CrUX?
Lighthouse suele medir solo la carga inicial. El CrUX mide toda la vida de la página, incluido lo que pasa después del desplazamiento y de las interacciones. Una diferencia grande sugiere desplazamientos posteriores a la carga.
Sobre el autor

Daniel Paz

Especialista en WordPress, rendimiento y SEO, fundador de WebOption

Daniel Paz es especialista en WordPress, rendimiento y SEO, fundador de WebOption y conferencista internacional. Con más de 18 años de experiencia en desarrollo web, ayuda a empresas a convertir sus sitios en plataformas rápidas, eficientes y orientadas a resultados.

Vea lo que su sitio necesita corregir.

Una verificación gratuita por ley, con la causa y la guía de corrección de cada problema.

Verificar mi sitio gratisCrear una cuenta gratis

Siga leyendo

Socios

Beneficios de socios con un solo cupón.

WPLIGHT

Enlaces de socios. Podemos recibir una comisión, sin costo adicional para ti.