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.
En esta página 8 min de lectura
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 conaspect-ratiocuando 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):
| Valor | Texto invisible hasta | Cambio a la fuente web | Efecto en el CLS |
|---|---|---|---|
block | 2 a 3 s | en cualquier momento | puede desplazar en el cambio |
swap | 0 ms | en cualquier momento | puede desplazar en el cambio |
fallback | 100 ms | hasta 3 s | puede desplazar en el cambio |
optional | 100 ms | no cambia después | no 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ó
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).
Lighthouse lista las imágenes sin
widthniheighty los elementos que se desplazaron durante la carga.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.
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.
Siga leyendo
- SEO técnico
Bots de IA en robots.txt: búsqueda, acceso del usuario y entrenamiento
GPTBot, ClaudeBot, OAI-SearchBot, Google-Extended: qué hace cada robot de IA, si respeta el robots.txt y cómo bloquear el entrenamiento sin dejar la búsqueda.
- SEO técnico
Redirecciones: cadenas, bucles y cómo corregirlos
Cómo trata Google los 301, 302, 307 y 308, qué causan las cadenas y los bucles en el rastreo y la velocidad, y cómo mapear, corregir y probar redirecciones.
- SEO técnico
llms.txt: qué es, qué no hace y cómo crearlo
El llms.txt resume el sitio para agentes de IA. Su formato, qué dicen Google y otras empresas, cuándo merece la pena crearlo y cómo escribir un buen archivo.