Cumulative Layout Shift (CLS)

Evitar los desplazamientos de los componentes en el diseño y mejorar la estabilidad visual de una página.

cumulative-layout-shift

El Cumulative Layout Shift (CLS) es una métrica de los Web Vitals que mide la estabilidad visual de una página web. Se refiere a los cambios inesperados en el diseño mientras se carga el contenido, lo que puede afectar la experiencia del usuario.

Si alguna vez has intentado hacer clic en un botón antes de que finalice la carga de la página y, en el último momento, este se ha movido porque otro elemento se cargó en su lugar, has experimentado CLS. Esto ocurre cuando los elementos de la página se reacomodan sin previo aviso debido a imágenes sin dimensiones definidas, fuentes que se cargan tarde o contenido insertado de forma dinámica sin espacio reservado.

Cómo reservar espacio para evitar CLS

El método más efectivo para evitar cambios inesperados en el diseño es asegurarse de que cada elemento tenga un tamaño predefinido. Aquí te dejamos algunas estrategias:

Reservar espacio para imágenes y videos

Las imágenes sin dimensiones explícitas hacen que el navegador no pueda calcular su espacio antes de que se descarguen. La solución más directa es definir el width y height en HTML <img src="/imagen.webp" width="32px" height="32px" /> o bien en CSS usando aspect-ratio:

img {
width: 100%;
aspect-ratio: 16 / 9; /* Mantiene la proporción sin distorsionar */
object-fit: cover;
}

Si el contenido multimedia se carga de forma dinámica y no sabemos el tamaño de antemano también podemos usar un min-height en el contenedor:

.video-placeholder {
min-height: 300px; /* Reserva el espacio */
background-color: #f0f0f0;
}

Evitar desplazamientos en listas dinámicas

Las listas que se rellenan con datos de forma asíncrona con JavaScript pueden generar saltos en la visualización si los elementos aparecen sin reservar espacio. Esto ocurre porque, al no haber contenido inicialmente, el espacio es mínimo y cuando los datos llegan, desplazan todo el contenido siguiente hacia abajo.

Para prevenir este efecto, hay un truco que es:

  • Usar placeholders o imagen de loading hasta que se cargue el contenido.
  • En un diseño mobile-first, usar height: 100vh en el loading para que el espacio de la lista a continuación quede fuera del viewport inicial y, con media queries, ajustar la altura en pantallas más grandes. Lo importante es que el min-height sea el mismo para el loading que para la lista dinámica.

Ejemplo:

<div class="dynamic-list-container">
<div id="loading" class="loading hidden">
<svg viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" stroke="#007bff" stroke-width="4" fill="none" stroke-linecap="round"></circle>
</svg>
</div>
<ul id="dynamicList" class="dynamic-list">
<li>Elemento 1</li>
<li>Elemento 2</li>
<li>Elemento 3</li>
<li>Elemento 4</li>
<li>Elemento 5</li>
</ul>
</div>

Los estilos principales CSS.

.loading {
min-height: 100vh;
/* En móviles, empuja la lista fuera del viewport mientras está cargando */
display: flex;
justify-content: center;
align-items: center;
}
@media (min-width: 768px) {
.loading {
min-height: 233px;
/* En pantallas más grandes, usamos un tamaño fijo */
}
}
.dynamic-list {
width: 100%;
display: flex;
flex-direction: column;
gap: 10px;
padding: 0;
list-style: none;
min-height: 233px;
}

Esta técnica evita que los cambios sean perceptibles en móviles y, en desktop, mantiene un tamaño fijo para evitar desplazamientos innecesarios.

Demostración

El ejemplo es un poco complejo, pero si visualizas la siguiente demo y pruebas con distintos tamaños de pantalla usando la herramienta de Google Developer Tools y refrescas la página varias veces verás que nunca hay un salto en el contenido cuando este se muestra.

Para ver bien el efecto deberías guardar este código html en local y ejecutarlo en el navegador.

Conclusión

El Cumulative Layout Shift es un problema común que afecta la experiencia del usuario y el rendimiento SEO. Aplicando buenas prácticas como reservar espacio con min-height, aspect-ratio y placeholders, es posible minimizar estos cambios y lograr una carga más estable y fluida.

Además hemos visto un ejemplo complejo pero efectivo para evitar el layout shift empujando el contenido fuera del viewport mientras este se está cargando.