Corrija o Layout shift culprits no Lighthouse e no PageSpeed
O que se moveu enquanto a página carregava, como verificar o elemento que o Lighthouse culpa e como corrigir mudanças causadas por imagens, fontes, anúncios, barras e CSS tardio.
Por que o Layout shift culprits fica vermelho no PageSpeed Insights?
O Layout shift culprits fica vermelho quando as mudanças de layout durante o carregamento somam um CLS de 0,1 ou mais: "Layout shifts ocorrem quando elementos se movem sem nenhuma interação do usuário." O Lighthouse chamava isso de Avoid large layout shifts. O diagnóstico lista o que se moveu e aponta a provável causa. Reserve espaço para o que chega depois: width e height em imagens, altura em slots de anúncios.
Quer apenas a solução rápida? Vá direto para imagens, web fonts, anúncios e embeds ou banners.
Como ler a tabela do Layout shift culprits?
O Lighthouse registra um carregamento da página em tela de celular e lista as mudanças de layout em rajadas (mudanças com intervalo menor que um segundo entre si), uma tabela por rajada. O aviso fica vermelho quando a pior rajada pontua 0,1 ou mais, e cinza abaixo disso. Busque o 0 mesmo quando estiver cinza: um deslocamento abaixo de 0,1 ainda move o texto que o visitante está lendo.
Em uma página de receita que criei nos moldes de um blog do WordPress — com título em web font, plugin de newsletter, anúncio sob o cabeçalho e plugin de receita —, o aviso ficou vermelho, com uma rajada de 0,241.

O aviso da página de receita no Lighthouse 13,5, celular, throttling simulado como no PageSpeed Insights.
- Total é a pontuação da rajada; o Total da pior rajada é o CLS informado pelo Lighthouse.
- Element é o que mais se moveu naquele deslocamento, muitas vezes um contêiner grande como
<main>. O elemento que o empurrou geralmente fica acima dele. - Layout shift score é a parcela daquele deslocamento no Total.
- A linha sob cada item é a provável causa apontada pelo Lighthouse: "Unsized image element" com a imagem, ou "Web font" com o arquivo de fonte. Uma linha pode exibir ambos ou nenhum.
Na página de receita, o Lighthouse apontou a imagem errada em três dos quatro deslocamentos: verifique qual elemento realmente se moveu antes de alterar qualquer coisa.
Por que a página pula durante o carregamento?
O visitante começa a ler e o texto sai do lugar, ou tenta tocar em um link e acerta o que se moveu para o espaço dele.
O navegador renderiza a página com o que tem em mãos. Qualquer conteúdo que chegue mais tarde sem espaço reservado empurra o que está abaixo: uma imagem sem dimensões começa com altura zero; uma web font renderiza o título mais largo do que o fallback. Cada movimento é uma mudança de layout, pontuada pela área da tela afetada e pela distância do deslocamento. Mudanças com menos de um segundo de intervalo formam uma rajada, e o CLS é a pontuação da pior rajada. Por isso, pequenos deslocamentos próximos se somam.
Na página de receita, cada causa fica abaixo de 0,1 isoladamente, e as quatro ocorrem na mesma rajada:
| Causa na página de receita | CLS isolado | Depois do fix |
|---|---|---|
| Web font do título | 0,035 | 0 |
| Foto da receita sem width e height | 0,036 | 0 |
| Barra de newsletter acima do cabeçalho | 0,058 | 0 |
| Anúncio em slot vazio | 0,054 | 0 |
| As quatro juntas | 0,241 | 0 |
Juntas, as quatro pontuam mais do que a soma das linhas porque, na página completa, a fonte e a foto deslocam uma fatia maior da tela. Corrigir as quatro causas levou a nota de Performance no celular de 87 para 100, sem mexer no FCP, LCP e Total Blocking Time. O CLS responde por um quarto dessa pontuação, mas o Google classifica a página pelo CLS que os visitantes reais recebem.

Antes, no first paint

Antes, aos 2 segundos

Depois, no first paint

Depois, aos 2 segundos
A página de receita no tamanho de tela de celular do Lighthouse. A caixa tracejada marca onde a foto estava no first paint.
O Lighthouse culpou o elemento certo?
O Lighthouse deduz a causa pelo timing: uma web font que terminou de baixar logo antes da mudança, uma imagem que o navegador renderizou logo depois. Por isso, uma imagem que só foi empurrada pode aparecer como "Unsized image element". A imagem de destaque da página de receita tem width="1200" height="675", mas o Lighthouse a lista sob as mudanças causadas pela fonte, pela barra de newsletter e pelo anúncio. Se uma linha não tiver indicação de causa abaixo, comece pela etapa 2.
- A imagem indicada tem dimensões? O item "Image elements do not have explicit width and height", em Diagnostics no mesmo relatório, lista as imagens sem dimensões. No DevTools, a aba Computed da imagem mostra a proporção em aspect-ratio (auto 1200 / 675) quando o espaço está reservado, e apenas auto quando não está. Uma imagem com dimensões definidas não causou o deslocamento, a menos que o tema aplique width: auto (Unsized image element abaixo): verifique a outra indicação sob a linha, geralmente "Web font", ou passe para a etapa 2.
- O que apareceu naquele instante? Grave um reload no painel Performance. Cada mudança é um losango roxo na faixa Layout shifts; passe o mouse sobre um deles para ver a pontuação e uma captura daquele momento com a área deslocada destacada. Na página de receita, a mudança de 0,0583 mostra a barra verde de newsletter que tinha acabado de surgir no topo. Para mudanças durante a rolagem, clique em Record, role a página e clique em Stop.
- Bloqueie a requisição suspeita e recarregue. No painel Network, clique com o botão direito na requisição suspeita — como o arquivo da fonte ou a tag do anúncio —, escolha Block requests, depois Block request URL, e grave novamente. Se o losango sumir, ele pertencia àquela requisição: bloquear a fonte na página de receita eliminou uma mudança e manteve as outras três. Depois, desmarque o bloqueio na gaveta Request conditions.

Etapa 2 na página de receita: Chrome 155, Slow 4G e 4× CPU slowdown.
Sem o DevTools, o cRUM no CoreDash faz essa checagem: ele coloca o trace do Chrome lado a lado com o que os visitantes reais vivenciaram, aponta o elemento que se moveu e mostra o que o visitante fez logo antes. O screenshot abaixo é de outra página: comece por Chrome trace e Real users.

cRUM do CoreDash em uma página de acme-pagespeed.com, celular.
Quais elementos se movem para os visitantes reais?
O Lighthouse carrega a página uma única vez, como um visitante novo, e nunca rola a tela nem clica. O field data registra cada visita real, incluindo o anúncio no meio do post que preenche o espaço quando o visitante rola até ele e o cabeçalho que encolhe na rolagem. Quando o Lighthouse está limpo ou cinza e o Search Console diz que o CLS é ruim, as mudanças geralmente acontecem após o carregamento.
No CoreDash, defina o dispositivo como mobile e agrupe o CLS por Attribution Element (CLS) para ver quais elementos se moveram; a dimensão Last interaction (CLS), em More dimensions, indica se isso aconteceu após uma rolagem. Para descobrir o que empurrou esses elementos, grave a página no painel Performance enquanto rola a tela (etapa 2 da checagem acima).

CoreDash no celular, p75: comece pelo maior CLS.
Como corrigir o Layout shift culprits?
Comece pela maior mudança na tabela com o maior Total, assim que a checagem acima confirmar a causa.
Unsized image element
Uma imagem sem dimensões começa com altura zero e empurra tudo o que estiver abaixo ao carregar. O WordPress adiciona width e height às imagens da biblioteca de mídia; imagens inseridas diretamente pelo tema, page builder, slider ou plugin costumam vir sem esses atributos, como a foto do cartão de receita. Se você não puder alterar o tema, o WP Rocket insere as dimensões ausentes (Media, Add missing image dimensions); em um template, declare você mesmo:
<img src="shakshuka.jpg" width="800" height="600" alt="Shakshuka in the pan">
img { max-width: 100%; height: auto; } O navegador converte os dois atributos em uma proporção e reserva a altura correta em qualquer largura de tela antes de o arquivo chegar. Um width: auto no CSS do tema anula essa proporção (auto sizing em imagens). Na Shopify, o filtro image_tag adiciona o tamanho; temas ou apps que geram a própria tag <img> precisam de width="{{ image.width }}" height="{{ image.height }}". No Next.js, o next/image exige width e height, ou fill dentro de um elemento pai com dimensões definidas.
Web font
O título aparece primeiro na fonte de fallback e depois na web font, que é mais larga ou mais estreita: as quebras de linha mudam de posição e tudo abaixo se desloca. No WordPress (CSS adicional) e na Shopify (na folha de estilos do tema em Edit code — atenção: atualizar o tema pode desfazer isso), adicione um @font-face de fallback que ajuste o tamanho de uma fonte local às medidas da web font. No Next.js, o next/font adiciona um fallback compatível automaticamente. Veja a configuração de fonte do título na página de receita:
@font-face {
font-family: "Young Serif Fallback";
src: local("Times New Roman"), local("Liberation Serif");
size-adjust: 131%;
ascent-override: 79.85%;
descent-override: 27.94%;
line-gap-override: 0%;
}
h1 { font-family: "Young Serif", "Young Serif Fallback", serif; } Um size-adjust de 122% vindo de uma calculadora ainda deixava um CLS de 0,029 ali; 131% levou a 0. O guia de font display traz a font face para Android, mostra como encontrar seus próprios números e explica o font-display: optional, que impede o deslocamento exibindo o fallback para visitantes novos.
Anúncios, vídeos e outros embeds
O script de anúncios insere o iframe no slot após o leilão; se o slot estiver vazio, tudo abaixo cai na medida da altura do anúncio. Segundo o State of Web Vitals, 89% dos sites sem redes de anúncios passam no CLS em celulares, contra 73% dos sites com duas ou mais redes. Isso é uma correlação.
No tema ou no plugin de anúncios, defina para o contêiner do anúncio a altura do formato mais exibido e transforme o iframe em bloco:
.ad-slot { min-height: 50px; }
.ad-slot iframe { display: block; } Por padrão, um iframe se alinha à linha de texto; por isso, na página de receita, o slot reservado ainda crescia 8 pixels até transformarmos o iframe em bloco. Mantenha o slot aberto quando não houver anúncio: recolhê-lo move a página tanto quanto preenchê-lo. Os anúncios automáticos do AdSense entram onde o Google decide: exclua as áreas da primeira tela (Ad settings, Excluded areas) e posicione ali uma unidade manual de anúncio em um slot com dimensões definidas.
YouTube, mapas e outros embeds também são iframes, e em um iframe os atributos width e height não definem uma proporção: com height: auto, ele encolhe para cerca de 150 pixels. Defina um aspect-ratio no iframe ou use uma facade — uma miniatura com tamanho definido que vira o iframe ao ser clicada (YouTube, Google Maps):
.video-embed iframe { width: 100%; height: auto; aspect-ratio: 16 / 9; } Barras e banners inseridos por scripts
Uma barra de newsletter ou um aviso de frete grátis que um script insere acima do cabeçalho empurra a página inteira para baixo.
Comece pelas configurações de layout do plugin ou app: uma barra inferior, um card ou um popup flutuam sobre a página e não movem nada. Ferramentas de consentimento como OneTrust e CookieYes oferecem os mesmos formatos; o Lighthouse vê o banner delas em cada execução porque sempre inicia sem cookies. Se a barra precisa mesmo ficar acima do cabeçalho, reserve o espaço dela no tema com a altura que a aba Computed exibe na largura de celular:
<div id="promo-slot" style="min-height: 48px"></div> Para uma barra que só aparece em algumas visitas, deixe-a flutuante ou renderize-a no HTML direto no servidor. Conteúdo visível disparado por uma tag do Google Tag Manager sempre chega após o first paint: mova-o para o template. Na Shopify, apps que injetam widgets — como estrelas de avaliação ou barra de avisos — precisam de uma altura definida no contêiner do app block.
CSS que carrega após o first paint
Uma folha de estilos carregada com o truque do media="print", ou adiada ou carregada de forma assíncrona por um plugin de otimização, chega depois que a página já está na tela e redimensiona o que já foi renderizado: um cabeçalho que expande empurra tudo o que estiver abaixo. No plugin, exclua esse arquivo da opção que adia o CSS ou o carrega de forma assíncrona; no tema, carregue-o com uma tag <link rel="stylesheet"> simples no head.
Animações, sliders e blocos renderizados por JavaScript
Geralmente esses elementos vêm do tema, de um plugin ou de um app: procure por uma configuração neles. Se não houver nenhuma, envie a um desenvolvedor o elemento indicado no relatório e o código abaixo.
Animar propriedades como top, left, height ou margin move o layout a cada frame. O Lighthouse lista isso em várias linhas pequenas sem indicar a causa, e o diagnóstico "Avoid non-composited animations" aponta o elemento. Anime transform em vez disso: ele move os pixels do elemento sem mexer em nada ao redor, e o navegador não contabiliza nenhuma mudança de layout.
.promo { position: fixed; top: 0; left: 0; right: 0;
transform: translateY(-100%); transition: transform .3s; }
.promo.is-open { transform: translateY(0); } Um slider exibe os slides empilhados até que o script execute; mantenha exibido apenas o primeiro slide até o script adicionar a classe de pronto (use a classe do próprio slider):
.slider:not(.is-ready) > :not(:first-child) { display: none; } Um bloco que o JavaScript renderiza após o carregamento, como avaliações ou produtos relacionados, precisa de um contêiner vazio na altura final ou de renderização no servidor. Para animações na rolagem, veja animações disparadas por rolagem e transições CSS.
Mudanças causadas por código que você não pode editar
Não dá para mudar o que um criativo de anúncio, um app sem ajustes de layout ou um script de terceiros insere. Reserve a altura que ele costuma ocupar, posicione-o abaixo da primeira tela ou substitua-o por uma alternativa que respeite o próprio espaço.
As alterações zeraram o CLS?
Verifique a correção no Lighthouse no celular, após limpar o plugin de cache e a CDN. Com todos os deslocamentos eliminados, o Layout shift culprits aparece como aprovado e o CLS vai a 0. Uma gravação no painel Performance — feita uma vez com reload e outra durante a rolagem — fica com a faixa Layout shifts vazia. O CoreDash exibe o novo CLS por elemento a partir das próximas visitas; o Search Console leva até 28 dias.

Antes

Depois
Antes e depois dos quatro fixes: Lighthouse 13,5, celular, throttling simulado.
Alguns deslocamentos vêm de pontos inacessíveis: um slot preenchido pela parceira de anúncios, um app que injeta um widget, uma mudança que só visitantes reais enxergam após rolar a página. Em uma Core Web Vitals power hour, analisamos esses pontos no field data e em uma gravação da página para definir a altura de cada contêiner.

Precisa de um site mais rápido?
Sou o Arjen e há 17 anos conserto sites lentos. Se você está com problemas que não consegue resolver sozinho, me envie sua URL e vamos conversar.
Me envie sua URL
Por que nosso fix não está funcionando?
Às vezes a CDN ainda entrega o HTML antigo, ou o fix entrou no template de desktop e o celular usa outro. Me envie sua URL e o problema, e eu respondo com alguns horários. Primeiro analiso o field data, depois dedicamos uma hora ao seu site, e em até um dia você tem minhas anotações por escrito. €350 sem IVA.
Me envie sua URLSem desenvolvedor para fazer o fix? Eu mesmo faço o trabalho, em uma cópia do seu site.