Como corrigir o CLS: o que faz a página pular

O que conta no CLS, as causas comuns (imagens, anúncios, fontes, animações) e como reservar espaço em HTML e CSS. Como achar o elemento que se move.

Checar meu site grátisVer os planos
Nesta página 7 min de leitura
  1. Como o CLS é calculado
  2. Causas comuns e como corrigir
  3. Como achar o elemento que se moveu
  4. Cache de voltar e avançar (bfcache)
  5. Como verificar o seu site

O CLS (Cumulative Layout Shift) mede quanto o conteúdo visível muda de lugar sem que o usuário tenha pedido. É bom até 0,1 e ruim acima de 0,25, no percentil 75 das visitas (web.dev). As causas mais comuns são imagens, anúncios e iframes sem tamanho definido, conteúdo inserido depois do carregamento, troca de fonte e animações que mexem no layout. Quase todas se resolvem reservando o espaço do elemento antes de ele chegar.

Este texto segue os guias do web.dev sobre CLS e otimização de CLS (atualizado em fevereiro de 2025). Para o contexto das três Core Web Vitals, veja o guia de Core Web Vitals.

Como o CLS é calculado

Um deslocamento de layout acontece quando um elemento visível muda de posição entre dois quadros. Cada deslocamento recebe uma pontuação:

pontuação = fração de impacto × fração de distância

  • A fração de impacto é a área visível ocupada pelos elementos que se moveram (somando a posição antiga e a nova) dividida pela área da tela.

  • A fração de distância é o maior movimento de um desses elementos, na horizontal ou na vertical, dividido pela maior dimensão da tela.

Os deslocamentos são agrupados em janelas de sessão. Uma janela junta deslocamentos com menos de 1 segundo entre um e outro e dura no máximo 5 segundos. O CLS da página é a soma da janela com a maior pontuação, e não a soma de todos os deslocamentos da visita (web.dev). Por isso uma página aberta por muito tempo não acumula CLS sem limite.

Deslocamentos esperados não contam

Se o usuário clica num botão que abre um painel, o conteúdo se mover é o resultado esperado. A API marca com hadRecentInput os deslocamentos que acontecem até 500 ms depois de uma interação discreta (toque, clique ou tecla), e eles ficam fora do CLS. Rolagem, arrastar e gesto de pinça não abrem essa janela (web.dev). Se a resposta a um clique demora mais que 500 ms para mudar o layout, como num "carregar mais" que espera a rede, o deslocamento passa a contar. Nesses casos, reserve o espaço ou mostre um indicador de carregamento logo no clique.

Causas comuns e como corrigir

Imagens sem largura e altura

Sem dimensões, o navegador não sabe quanto espaço reservar e só descobre quando a imagem chega, empurrando o texto para baixo. A correção é declarar width e height no HTML. Os navegadores atuais usam esses dois valores para calcular a proporção e reservar o espaço, mesmo quando o CSS deixa a imagem responsiva (web.dev):

<img src="/img/produto.jpg" width="640" height="360" alt="Descrição do produto">
img {
  max-width: 100%;
  height: auto;
}

O navegador aplica internamente aspect-ratio: auto 640 / 360. A palavra auto faz a proporção real do arquivo valer depois que ele baixa. Os valores de width e height não precisam ser o tamanho exibido, só precisam ter a proporção certa.

Em imagens responsivas, todas as opções do srcset devem ter a mesma proporção. Se cada tamanho de tela usa um recorte diferente, declare as dimensões em cada <source> do <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="Descrição do banner">
</picture>

Para elementos que não têm atributos de dimensão, como um contêiner de vídeo, a propriedade CSS aspect-ratio reserva o espaço. Ela funciona no Chrome 88, no Firefox 89 e no Safari 15 em diante (MDN browser-compat-data).

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

Anúncios, embeds e iframes

Blocos de anúncio, vídeos incorporados e widgets de terceiros costumam chegar depois do conteúdo e com tamanho variável. O web.dev recomenda (web.dev):

  • reservar o espaço com min-height, ou com aspect-ratio quando o conteúdo é responsivo;

  • usar como base o tamanho do menor criativo esperado, e media queries quando o tamanho muda entre celular e desktop;

  • manter um placeholder quando nenhum anúncio é entregue, em vez de fechar o espaço, porque remover o espaço reservado também causa deslocamento.

/* Os valores são exemplo: use o tamanho do menor anúncio de cada posição */
.anuncio-topo {
  min-height: 100px;
}

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

Conteúdo inserido depois do carregamento

Avisos, faixas de promoção, formulários e listas que aparecem por JavaScript empurram o que está abaixo se não tiverem espaço reservado. As opções do web.dev:

  • reservar o espaço do elemento desde o HTML inicial;

  • posicionar o conteúdo tardio mais abaixo na página, onde o deslocamento afeta menos área visível;

  • em listas e feeds, trocar o conteúdo dentro de um contêiner de tamanho fixo, ou deixar o usuário carregar mais com um botão (o deslocamento logo após o clique não conta);

  • carregar o conteúdo novo fora da tela e avisar que há novidades, em vez de inseri-lo no topo.

Fontes web

Enquanto a fonte web baixa, o navegador mostra o texto invisível ou com uma fonte reserva. Quando a fonte web chega e tem medidas diferentes da reserva, as linhas mudam de largura e altura e o conteúdo se move. O comportamento é controlado por font-display (web.dev):

ValorTexto invisível por atéTroca pela fonte webEfeito no CLS
block2 a 3 sa qualquer momentopode deslocar na troca
swap0 msa qualquer momentopode deslocar na troca
fallback100 msaté 3 spode deslocar na troca
optional100 msnão troca depoisnão desloca por troca de fonte

Com optional, a fonte web só é usada se estiver pronta no primeiro layout. Se a marca exige a fonte web sempre, use swap e aproxime a fonte reserva das medidas dela. Isso se faz com uma regra @font-face para a fonte local de reserva, ajustada com size-adjust e com as propriedades de métrica ascent-override, descent-override e 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;
}

Os valores acima são os do artigo do Chrome para a Poppins sobre a Arial; cada par de fontes tem os seus, calculados a partir das métricas dos arquivos. Uma reserva diferente, como a Roboto no Android, precisa de outro conjunto. Sobre suporte, segundo os dados do MDN: size-adjust funciona no Chrome 92, no Firefox 92 e no Safari 17 em diante; as três propriedades de override existem no Chrome e no Firefox, mas no Safari aparecem só na versão de testes (Technology Preview) (MDN browser-compat-data, consultado em outubro de 2026). No Safari estável, portanto, só o size-adjust vale.

O web.dev também recomenda o preload das fontes críticas com <link rel="preload">, para que cheguem mais cedo. O preload disputa banda com outros recursos; use só para as fontes da primeira tela. A mesma escolha de font-display afeta o LCP quando o maior elemento é texto (veja como melhorar o LCP).

Animações que mexem no layout

Animar propriedades de posição e tamanho, como top, left, width e height, recalcula o layout e pode gerar deslocamentos. Use transform (translate, scale, rotate), que move o elemento sem mexer no fluxo da página (web.dev):

/* Evite */
.aviso { position: relative; transition: top 0.3s; }
.aviso.aberto { top: 0; }

/* Prefira */
.aviso { transition: transform 0.3s; transform: translateY(-100%); }
.aviso.aberto { transform: translateY(0); }

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

Como achar o elemento que se moveu

  1. No Chrome DevTools, a visão de métricas ao vivo do painel Performance mostra o CLS local e uma lista de deslocamentos com o elemento, o momento e a pontuação. Numa gravação, a faixa Layout Shifts agrupa os deslocamentos e o insight "Layout shift culprits" aponta as causas prováveis (web.dev).

  2. O Lighthouse lista imagens sem width e height e os elementos que se deslocaram no carregamento.

  3. Para dados de usuários reais, a biblioteca web-vitals na versão de atribuição informa o elemento do maior deslocamento, quando ele ocorreu, quanto pontuou e em que fase do carregamento a página estava (GitHub GoogleChrome/web-vitals).

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

onCLS(({value, attribution}) => {
  console.log('CLS', value);
  console.log('Elemento', attribution.largestShiftTarget);
  console.log('Quando (ms)', attribution.largestShiftTime);
  console.log('Pontuação', attribution.largestShiftValue);
  console.log('Estado da página', attribution.loadState);
}, {reportAllChanges: true});

A opção reportAllChanges ajuda a depurar porque avisa a cada aumento do CLS; o README da biblioteca não recomenda usá-la em produção. A API que mede os deslocamentos (Layout Instability) só existe nos navegadores Chromium: a web-vitals informa suporte de onCLS() apenas no Chromium (GitHub GoogleChrome/web-vitals). Os dados de campo de CLS, no RUM ou no CrUX, vêm desses navegadores. A diferença entre dado de laboratório e de campo está em Lighthouse, CrUX e RUM.

Cache de voltar e avançar (bfcache)

Quando o usuário volta para uma página que estava no bfcache, ela é restaurada da memória exatamente como estava, sem repetir os deslocamentos do carregamento, e o CLS dessa visita recomeça do zero (web.dev). Por isso vale deixar as páginas aptas a entrar nesse cache. Os impedimentos mais comuns são listeners de unload (troque por pagehide), o cabeçalho Cache-Control: no-store no HTML e conexões abertas (IndexedDB, WebSocket, requisições em andamento) no momento em que o usuário sai. No Chrome DevTools, em Application, a seção Back/forward cache tem um teste que mostra se a página foi restaurada e por que não.

Como verificar o seu site

No WebVyze, o diagnóstico de CLS usa o Google PageSpeed para mostrar a situação da página, e o teste de Core Web Vitals traz o CLS do CrUX dos últimos 28 dias, no celular e no desktop, para até 10 URLs. As duas ferramentas são grátis. Nos planos pagos, um script de visitantes reais baseado na biblioteca web-vitals registra o CLS de cada visita com a causa. Para problemas de resposta ao clique, veja como melhorar o INP.

Perguntas frequentes

O CLS tem unidade, como segundos?
Não. É um número sem unidade que combina quanto da área visível se moveu e a distância do movimento.
Rolar a página conta como interação para excluir o deslocamento?
Não. A exclusão de 500 ms vale para toques, cliques e teclas. Rolagem, arrastar e gesto de pinça não contam.
Deslocamentos dentro de iframes entram no CLS?
Entram no CLS do CrUX, porque o usuário vê esses movimentos. As APIs do navegador não conseguem medi-los, então a biblioteca web-vitals não os enxerga.
Por que o CLS do Lighthouse é menor que o do CrUX?
O Lighthouse costuma medir só o carregamento inicial. O CrUX mede a vida inteira da página, inclusive o que acontece depois da rolagem e das interações. Uma diferença grande sugere deslocamentos depois do carregamento.
Sobre o autor

Daniel Paz

Especialista em WordPress, performance e SEO, fundador da WebOption

Daniel Paz é especialista em WordPress, performance e SEO, fundador da WebOption e palestrante internacional. Com mais de 18 anos de experiência em desenvolvimento web, ajuda empresas a transformar sites em plataformas rápidas, eficientes e orientadas a resultados.

Veja o que o seu site precisa corrigir.

Checagem grátis por lei, com a causa e o guia de correção de cada problema.

Checar meu site grátisCriar conta grátis

Continue lendo

Parceiros

Benefícios de parceiros com o mesmo cupom.

WPLIGHT

Links de parceiros. Podemos receber uma comissão, sem custo adicional para você.