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.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-11-05

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.

Lighthouse's Layout shift culprits insight in red on a recipe page: Total 0.241 and four shifts. The moved elements are div.entry-content, main, the recipe card title and article. Under three of them the cause line is Unsized image element pointing at the featured image, which has width 1200 and height 675; the font row also says Web font, and the recipe card row names the recipe photo.

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 receitaCLS isoladoDepois do fix
Web font do título0,0350
Foto da receita sem width e height0,0360
Barra de newsletter acima do cabeçalho0,0580
Anúncio em slot vazio0,0540
As quatro juntas0,2410

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.

The recipe page on a phone at its first paint: header, an empty ad label, the headline in two lines in the fallback font, the author line and the featured photo.

Antes, no first paint

The same page two seconds later: a green newsletter bar above the header, an ad under it, the headline in three lines in the web font, and the photo lower than the dashed box that marks where it was at first paint.

Antes, aos 2 segundos

The fixed page at its first paint: an empty strip where the bar will go, an empty ad slot at its full height, and the headline already in three lines in the adjusted fallback font.

Depois, no first paint

The fixed page two seconds later: the bar and the ad have filled their space, the headline is in the web font, and the photo is where it was at 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.

  1. 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.
  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.
  3. 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.
Chrome DevTools Performance panel: the Layout shifts track with four diamonds in the worst layout shift cluster, the tooltip of the 0.0583 shift showing the page with a green newsletter bar that has just appeared above the header, and the Layout shift culprits insight on the left listing two unsized images and a web font.

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.

CoreDash cRUM for one page on mobile: Chrome trace on top, and under Real users the panels When it shifts, What shifts and What preceded it.

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, Cumulative Layout Shift by Attribution Element (CLS) on mobile: per element the p75 CLS, a performance label, the distribution and the sessions; the rows include main#main.site-main, the recipe name, an in-content ad slot, the nav and the footer.

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.

Lighthouse at phone width on the recipe page before the fixes: score 87, CLS 0.241 in orange, every other metric green, and Layout shift culprits in red.

Antes

Lighthouse at phone width on the same page after the fixes: score 100, CLS 0, and Layout shift culprits under the passed insights.

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.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

 
Arjen Karel

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
Arjen Karel
Arjen Karel, Consultor de web performance desde 2009

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 URL

Sem desenvolvedor para fazer o fix? Eu mesmo faço o trabalho, em uma cópia do seu site.

Corrija o Layout shift culprits no Lighthouse e no PageSpeed Core Web Vitals Corrija o Layout shift culprits no Lighthouse e no PageSpeed