Os erros de dimensão de imagem e o Cumulative Layout Shift (CLS) estão entre os avisos mais frequentes que os webmasters veem na Google Search Console e no PageSpeed Insights. Este guia mostra como encontrar as causas no WordPress, corrigir os templates e o fluxo de tratamento de imagens e medir o CLS de forma reproduzível, em vez de pôr uma única URL «verde» por cosmética.
O CLS mede a estabilidade visual: se algo salta no viewport enquanto a página carrega, a pontuação sobe. Os suspeitos habituais são width/height em falta no <img>, imagens hero que carregam tarde, banners de cookies e espaços publicitários sem altura reservada. Documentação oficial para aprofundar: web.dev sobre o CLS, web.dev sobre a otimização do CLS, MDN aspect-ratio e wp_get_attachment_image().
Em lojas e temas de revista, o mesmo padrão de erro repete-se: o tema personalizado escreve as imagens com markup fixo, a redação carrega originais de 5000 píxeis e um plugin de otimização comprime os ficheiros sem nunca escrever as dimensões na árvore HTML. O PageSpeed fica então «verde» numa URL de demonstração e continua vermelho no CrUX, porque os templates de categoria e de produto usam outros caminhos de imagem.
O que significam os erros de dimensão de imagem no PageSpeed Insights
Durante anos, o Lighthouse avisava que os elementos de imagem não tinham largura e altura explícitas quando o HTML não incluía as dimensões. Nesse caso, o browser só conhece a proporção depois do download e empurra o texto e os botões para baixo. Em páginas de produto com muitas miniaturas, isto soma-se até um CLS vermelho.
Desde o Lighthouse 13, estas verificações têm outro nome. Segundo o Chrome for Developers, as auditorias unsized-images e layout-shifts passaram a fazer parte da insight cls-culprits-insight, e prioritize-lcp-image e lcp-lazy-loaded passaram para lcp-discovery-insight. Quem procurar o nome antigo da auditoria em tickets antigos ou relatórios de agência já não o encontra no relatório atual. A causa continua a ser a mesma.
No WordPress, os erros surgem muitas vezes assim:
- Templates personalizados usam
<img src="...">escrito à mão, sem atributos - Construtores de páginas guardam o HTML da imagem sem dimensões nos metadados do artigo
- Ícones SVG sem viewBox e sem tamanho definido em CSS
- Imagens de fundo em CSS não podem definir
width/heightno markup (o espaço tem de ser reservado pelo contentor) - Shortcodes de galeria de plugins antigos renderizam
<img>sem a API de anexos - URLs de CDN que apenas espelham o ficheiro e perdem os metadados da biblioteca multimédia
Outro caso especial: imagens externas (hotlinking para PDFs de fabricantes ou CDNs de redes sociais). O WordPress não conhece as suas dimensões. Ou as aloja localmente e as trata como anexo, ou protege o contentor à volta com uma proporção fixa e min-height.
Como a Google calcula o CLS
Cada deslocamento individual recebe uma pontuação a partir de dois fatores: Layout-Shift-Score = Impact Fraction × Distance Fraction. A impact fraction é a parte do viewport que o elemento instável ocupa nos dois frames em conjunto. A distance fraction é a maior distância que o elemento percorre, relativa à maior dimensão do viewport. Um elemento que, somado à nova posição, cobre metade do viewport e se desloca 15 % da altura do viewport resulta em 0,5 × 0,15 = 0,075.
O CLS já não soma estes valores ao longo de toda a vida da página. O web.dev define o CLS como a maior rajada de deslocamentos: os shifts separados por menos de um segundo formam uma janela de sessão, e uma janela dura no máximo cinco segundos. Conta a janela com a soma mais alta. Uma página longa com muitos deslocamentos pequenos e separados no tempo não fica, por isso, automaticamente vermelha.
Duas exceções são importantes para quem desenvolve temas. Os deslocamentos nos 500 ms seguintes a uma interação discreta (toque, clique, tecla) recebem a flag hadRecentInput e não entram no CLS. Fazer scroll, arrastar e fazer zoom com dois dedos não contam como esse tipo de interação. E as animações com transform não deslocam outros elementos, por isso, segundo o web.dev, não contam para o CLS.
A Google avalia os dados de campo (CrUX) com mais rigor do que uma única execução do Lighthouse em laboratório. O que conta é o percentil 75 das visualizações de página, separado entre telemóvel e desktop. Por isso, depois das correções meça tipos de página reais (página inicial, artigo, categoria, produto, carrinho), e não apenas a URL que por acaso ficou verde.
| CLS (bom / a melhorar / mau) | Causa típica nas imagens |
|---|---|
| ≤ 0,1 | Dimensões e aspect-ratio definidos |
| 0,1-0,25 | Templates mistos, banners tardios |
| > 0,25 | Hero sem dimensões, anúncios sem espaço reservado, fontes e imagens em conjunto |
Atenção: o CLS de laboratório e o CLS de campo podem divergir. Em laboratório faltam muitas vezes os banners de consentimento, os testes A/B e os scripts de terceiros lentos. Se a Search Console indicar «mau» e o Lighthouse indicar «bom», confie nos dados de campo e abra as Layout Shift Regions num dispositivo móvel real ou numa emulação realista.
Como o browser deriva a proporção a partir de width e height
Segundo a MDN, os atributos width e height são números inteiros sem unidade e indicam o tamanho intrínseco em píxeis. Não definem o tamanho com que a imagem acaba por ser apresentada. A sua utilidade: o browser calcula a proporção a partir deles antes de a imagem carregar. Os browsers atuais definem internamente um valor como aspect-ratio: auto 640 / 360 (web.dev).
O auto neste valor tem um significado preciso. A MDN descreve-o assim: num elemento substituído como <img>, aplica-se a proporção indicada até o conteúdo carregar e, depois disso, a proporção natural do ficheiro. Se os atributos e o ficheiro não coincidirem, a imagem salta na mesma depois de carregar. É exatamente o que acontece quando um template escreve nos atributos as dimensões do original, mas serve um tamanho com outro recorte.
Daqui resultam três regras:
- Todos os candidatos num
srcsetprecisam da mesma proporção, caso contrário os atributos só estão corretos para um deles. - Em art direction com
<picture>e recortes diferentes, cada<source>recebe os seus próprioswidth/height. Segundo o web.dev, o Chrome, o Firefox e o Safari já suportam isto. - As imagens com lazy loading precisam dos atributos com especial urgência. A MDN avisa que uma imagem ainda não carregada e sem dimensões tem 0 × 0 píxeis e, por isso, pode nunca entrar na área visível que desencadearia o carregamento.
Uma regra muito difundida, mas sem efeito, é aspect-ratio: attr(width) / attr(height). Sem indicação de tipo, attr() devolve, segundo a MDN, uma string CSS, e uma string não é uma proporção válida. A linha é descartada. O browser faz a derivação sozinho de qualquer forma, por isso a regra pode sair.
Como corrigir o CLS de imagens no markup HTML
1. Definir width e height
<img src="imagem.jpg" width="800" height="600" alt="Descrição">No WordPress, use de preferência a função do núcleo, que vai buscar as dimensões aos metadados do anexo:
echo wp_get_attachment_image( $attachment_id, 'large' );wp_get_attachment_image() escreve width, height e, se existirem subformatos, srcset/sizes. A classe attachment-large size-large e o loading são definidos pelo próprio WordPress; um 'loading' => 'lazy' próprio não é necessário, porque desde a versão 6.3 é o núcleo que decide que imagens podem carregar em lazy. As tags <img> fixas em temas antigos são a razão mais comum para só os artigos novos estarem «limpos» enquanto o arquivo antigo produz CLS.
Se usa campos de imagem do ACF, guarde o ID do anexo e não apenas a URL. A partir do ID constrói wp_get_attachment_image(). A partir de uma URL simples só lhe resta adivinhar ou fazer um pedido extra à tabela de anexos.
2. CSS aspect-ratio para contentores e recortes
Para imagens de conteúdo com atributos corretos, basta uma regra responsiva de base. Uma proporção fixa em todas as imagens de conteúdo obrigaria os formatos verticais e os panoramas a 4:3 e deformá-los-ia:
.entry-content img {
max-width: 100%;
height: auto;
}
.hero-media img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
object-fit: cover;
}Um aspect-ratio fixo pertence a componentes com recorte intencional: hero, grelhas de cartões, mosaicos de produtos, vídeos incorporados e contentores com imagem de fundo. Combine-o com um sizes sensato, caso contrário o browser descarrega variantes demasiado grandes e o LCP sofre em paralelo com o CLS.
Antes do aspect-ratio, os temas reservavam espaço com o truque do padding-bottom: um wrapper com height: 0 e padding-bottom: 56.25% (9 a dividir por 16) e, dentro dele, uma imagem com posicionamento absoluto. Continua a funcionar, mas exige markup adicional. Se herdar temas antigos, vale a pena passar para aspect-ratio na próxima refatoração de templates, e não como projeto à parte.
3. Usar o lazy loading corretamente
O lazy loading pertence abaixo da dobra. O elemento LCP (muitas vezes a imagem hero) não pode ter loading="lazy". Caso contrário, o browser adia o download até o layout estar pronto.
No template, a forma mais direta é marcar explicitamente a imagem hero como importante:
echo wp_get_attachment_image(
$hero_id,
'full',
false,
array(
'class' => 'hero-media__img',
'fetchpriority' => 'high',
)
);Com fetchpriority="high", a wp_get_loading_optimization_attributes() trata a imagem como visível e omite loading="lazy". O caminho antes comum, através do filtro wp_get_attachment_image_attributes, que muda o loading para eager a posteriori, tem um problema desde o WordPress 6.7: nessa altura, o núcleo já colocou auto no início do sizes das imagens lazy. Segundo a dev note da 6.7, os browsers descartam sizes="auto, ..." numa imagem que não carrega em lazy e recorrem a 100vw. A imagem hero é então descarregada na maior variante. Se tiver de manter o filtro, remova o prefixo com:
add_filter( 'wp_get_attachment_image_attributes', function ( $attr ) {
if ( ! empty( $attr['class'] ) && false !== strpos( $attr['class'], 'skip-lazy' ) ) {
$attr['loading'] = 'eager';
$attr['fetchpriority'] = 'high';
if ( isset( $attr['sizes'] ) ) {
$attr['sizes'] = preg_replace( '/^\s*auto\s*,\s*/i', '', $attr['sizes'] );
}
}
return $attr;
} );Segundo a MDN, fetchpriority é apenas uma indicação ao browser, com os valores high, low e auto. Defina high em exatamente uma imagem por página, caso contrário vários downloads disputam a mesma prioridade.
Em temas de lojas vê-se com frequência que a primeira imagem de produto está em lazy, porque um filtro genérico marca todas as img. Uma exceção para o conteúdo acima da dobra poupa CLS e LCP. Verifique também os sliders: se os slides 2 e 3 estão logo em lazy mas o slide 1 não tem dimensões, toda a moldura do carrossel salta na mesma.
O que o WordPress faz automaticamente desde a 5.5, 6.3 e 6.7
Muitos tutoriais recomendam código para coisas de que o núcleo já trata há muito. O estado segundo as dev notes oficiais:
| Versão | Alteração | Fonte |
|---|---|---|
| 4.4 | srcset e sizes para imagens de anexo | wp_get_attachment_image |
| 5.5 | loading="lazy" para imagens com width e height; as dimensões em falta são acrescentadas em imagens com a classe wp-image-{ID} | Dev note 5.5 |
| 6.1 | Atributo decoding em wp_get_attachment_image() | Changelog da função |
| 6.3 | fetchpriority="high" para a provável imagem LCP, limite de imagens sem lazy loading de 1 para 3 | Dev note 6.3 |
| 6.7 | sizes="auto, ..." para imagens com lazy loading | Dev note 6.7 |
O acrescento das dimensões é feito em wp_filter_content_tags(), que em default-filters.php está ligado a the_content com prioridade 12, ou seja, depois dos shortcodes. O lazy loading deixa de fora as três primeiras imagens do conteúdo; o valor é controlado pelo filtro wp_omit_loading_attr_threshold. Para receber fetchpriority="high", uma imagem precisa, segundo a dev note da 6.3, de uma área de pelo menos 50 000 píxeis, ajustável através de wp_min_priority_img_pixels. Por isso, um logótipo pequeno não recebe a prioridade.
Consequência prática: as imagens inseridas através do editor de blocos com ligação à biblioteca multimédia raramente são o problema nas versões atuais. Os erros estão nos templates do tema, no output dos construtores de páginas, no HTML escrito à mão e em imagens sem a classe wp-image-{ID}.
Que tamanhos de imagem o WordPress cria no carregamento
Segundo o código-fonte do WordPress, uma instalação nova cria estes tamanhos: thumbnail 150 px, medium 300 px, medium_large 768 px e large 1024 px (sempre largura, alterável nas definições). Desde a 5.3 juntam-se 1536x1536 e 2048x2048. Também desde a 5.3, o WordPress reduz os originais cujo lado maior ultrapasse 2560 px para esse limite e guarda-os com o sufixo -scaled; o filtro big_image_size_threshold altera ou desativa o valor.
Para o srcset, a wp_calculate_image_srcset() só inclui variantes com a mesma proporção e, por predefinição, corta nos 2048 px de largura (filtro max_srcset_image_width). Os tamanhos próprios do tema registados com add_image_size() e recorte rígido (crop = true) têm uma proporção diferente do original e, por isso, não entram no srcset do original. Uma grelha de cartões com recorte 4:3 precisa, portanto, de vários tamanhos próprios na mesma proporção 4:3, caso contrário existe um único ficheiro para todos os ecrãs.
Como usar imagens responsivas e srcset no WordPress
O núcleo fornece srcset quando usa as APIs de anexos. Os construtores de páginas e os campos de imagem do ACF com URL manual contornam isso. Faltam então as dimensões e as variantes adequadas.
Como o browser escolhe: com descritores w no srcset, ignora o src e usa o sizes para determinar a largura de apresentação esperada. Se faltar o sizes, aplica-se, segundo a MDN, 100vw. Por isso, desde a 4.4 o WordPress define um valor predefinido que segue o padrão (max-width: {largura}px) 100vw, {largura}px; os temas devem ajustá-lo ao layout real através do filtro wp_calculate_image_sizes. Desde a 6.7, o núcleo coloca auto à frente nas imagens com lazy loading, para que o browser use a largura efetivamente renderizada.
Lista de verificação para a redação e para o desenvolvimento:
- Biblioteca multimédia: não carregar originais com 6000 px de largura sem necessidade
- O tema regista variantes
add_image_sizesensatas, na mesma proporção - Os templates nunca chamam apenas
wp_get_attachment_url()para imagens de conteúdo visíveis - Depois de mudar de tema, regenerar os subformatos (por exemplo,
wp media regenerate), caso contrário faltam candidatos ao srcset - Ajustar o atributo
sizesà largura real do layout (barra lateral ou largura total)
Um caso típico de revista: o conteúdo no desktop tem 720 px de largura e no telemóvel 100 vw. Se o sizes estiver em 100vw, o browser no desktop descarrega muitas vezes a variante de 2048 píxeis para uma coluna de 720 píxeis. Não é um bug de CLS, mas prolonga o LCP e dá a sensação de «página pesada». Pense no CLS e no LCP em conjunto, não um depois do outro.
Imagem menor do que o recomendado: requisitos para o Discover e o markup de artigo
O aviso da Search Console «imagem menor do que o recomendado» não tem nada a ver com o CLS. Diz respeito à imagem que a Google usa nas pré-visualizações, ou seja, a imagem de destaque, a og:image e o campo image no markup estruturado de artigo. Este aviso vem originalmente da era do AMP; os requisitos atuais estão em dois documentos da Google.
Para o Google Discover, as imagens grandes devem ter pelo menos 1200 px de largura, mais de 300 000 píxeis de área e uma proporção de 16:9. As pré-visualizações grandes só aparecem se a página permitir max-image-preview:large ou usar AMP. O logótipo do site não deve chegar à Google como imagem de pré-visualização, nem como og:image nem no markup Schema.
A documentação sobre o markup de artigo recomenda várias imagens de alta resolução com pelo menos 50 000 píxeis (largura vezes altura) nas proporções 16:9, 4:3 e 1:1. As URLs das imagens têm de ser rastreáveis e indexáveis.
Um script de verificação para a redação, que regista no log as imagens de destaque demasiado estreitas no momento da publicação:
add_action( 'publish_post', function ( int $post_id ) {
$thumbnail_id = get_post_thumbnail_id( $post_id );
if ( ! $thumbnail_id ) {
return;
}
$meta = wp_get_attachment_metadata( $thumbnail_id );
$width = (int) ( $meta['width'] ?? 0 );
if ( $width < 1200 ) {
error_log( sprintf( 'Artigo %d: imagem de destaque com apenas %d px de largura.', $post_id, $width ) );
}
} );Em carregamentos redimensionados, a wp_get_attachment_metadata() devolve as dimensões do ficheiro -scaled, ou seja, no máximo 2560 px. Para o Discover, é suficiente.
Como evitar o CLS em layouts WooCommerce e em grelha
As grelhas de produtos são pontos críticos de CLS. Cada mosaico precisa da mesma moldura de imagem. Se um produto não tiver imagem de destaque e o fallback for um PNG de substituição mais estreito, o cartão salta. Defina no tema um wrapper uniforme com aspect-ratio e object-fit: cover para as imagens de produto.
Variações e galerias: o WooCommerce troca as miniaturas por JS. Reserve a altura da galeria antes de os scripts de variação dispararem. Caso contrário, alguém clica em «Tamanho M», a imagem grande carrega depois e o botão de adicionar ao carrinho desloca-se. Os deslocamentos nos 500 ms seguintes ao clique não contam para o CLS, mas um carregamento posterior conta, e de qualquer forma o botão foge debaixo do dedo.
Como as fontes web, os banners e os scripts de terceiros causam CLS
Nem todo o CLS é um problema de imagens. Banners de cookies que saltam de 0 para 120 px depois do consentimento, fontes sem uma estratégia font-display adequada e widgets de afiliados tardios produzem as mesmas regiões de deslocamento nas DevTools. Reserve alturas mínimas para as barras de consentimento e os espaços publicitários antes de os scripts carregarem. O web.dev recomenda ainda não deixar o espaço reservado colapsar quando não é servido nenhum anúncio, mas mostrar um marcador de posição.
Chrome: DevTools, depois Rendering, depois Layout Shift Regions. Recarregue a página; as áreas azuis marcam os culpados. Atenção à emulação móvel: o CLS no desktop pode estar «verde» enquanto o CrUX móvel continua vermelho.
Outras fontes além das imagens:
- Fontes web que saltam do fallback para a fonte personalizada (
font-display: optional, segundo o web.dev, só usa a fonte se ela já estiver disponível no primeiro layout) - Vídeos e iframes incorporados sem uma caixa com aspect-ratio
- Cabeçalhos «sticky» que crescem consoante a posição do scroll
- Bolhas de chat ao vivo que só aparecem em período de inatividade
- Animações com
top,leftouheightem vez detransform
Como reparar artigos antigos de forma programática
Para imagens com a classe wp-image-{ID} não é preciso código próprio: desde a 5.5, a wp_filter_content_tags() acrescenta sozinha width/height em falta, e com as dimensões do tamanho efetivamente inserido. Um filtro que use em vez disso as dimensões de full escreve atributos errados numa miniatura de 300 px inserida no conteúdo.
O código próprio só compensa para tags sem essa classe, por exemplo de conteúdos importados ou de HTML antigo de construtores de páginas. O filtro seguinte corre depois do núcleo, procura o ID do anexo pela URL e usa as dimensões do tamanho correspondente:
add_filter( 'the_content', function ( $content ) {
if ( false === strpos( $content, '<img' ) ) {
return $content;
}
return preg_replace_callback(
'/<img\b[^>]*>/i',
function ( $matches ) {
$img = $matches[0];
if ( preg_match( '/\swidth=/i', $img ) || ! preg_match( '/\ssrc="([^"]+)"/i', $img, $src ) ) {
return $img;
}
// attachment_url_to_postid() só conhece a URL original, por isso removemos o sufixo de tamanho.
$original = preg_replace( '/-\d+x\d+(?=\.\w+$)/', '', strtok( $src[1], '?' ) );
$id = attachment_url_to_postid( $original );
if ( ! $id ) {
return $img;
}
$size = wp_image_src_get_dimensions( $src[1], wp_get_attachment_metadata( $id ), $id );
if ( ! $size ) {
return $img;
}
return str_replace( '<img', sprintf( '<img width="%d" height="%d"', $size[0], $size[1] ), $img );
},
$content
);
}, 13 );Isto é uma rede de segurança, não um substituto de templates bem feitos. A attachment_url_to_postid() faz uma consulta à base de dados por imagem; em arquivos grandes compensa uma execução única do WP-CLI que escreva os atributos no post_content, em vez de filtrar em cada pedido. As imagens de domínios externos ficam intactas: consultá-las em tempo de execução com getimagesize() descarrega o ficheiro do servidor externo a cada cache miss.
Como medir e verificar o CLS depois das correções
Depois de cada correção, não se limite a voltar a correr o Lighthouse. Um processo que funciona em projetos de agência:
- Abrir o relatório Core Web Vitals na Search Console e anotar os grupos de URLs afetados
- Verificar uma URL por tipo de template com as Layout Shift Regions
- Emular um Android de gama média (e não apenas um desktop com ecrã de alta densidade)
- Depois do deploy, esperar 28 dias de dados de campo antes de dar o trabalho como «concluído», porque o CrUX avalia uma janela móvel de 28 dias
- Regressões: um novo bloco hero ou uma nova CMP de cookies precisa da mesma verificação
Para dados de campo próprios, a biblioteca web-vitals da Google é adequada. Reporta o CLS com a mesma lógica de janelas de sessão do CrUX:
import { onCLS } from 'web-vitals';
onCLS( ( metric ) => {
navigator.sendBeacon( '/vitals', JSON.stringify( {
name: metric.name,
value: metric.value,
id: metric.id,
} ) );
} );Documente os tipos de página medidos. Se só a página inicial estiver verde e o artigo individual do blog continuar vermelho, o trabalho não está terminado. A redação e o desenvolvimento precisam de uma definição comum de «concluído».
Lista de verificação para erros de dimensão de imagem e CLS no WordPress
- Todas as imagens de conteúdo visíveis passam pelas APIs de anexos
- Hero sem
loading="lazy", comfetchpriority="high"em exatamente uma imagem - Nenhum
sizes="auto, ..."em imagens que não carregam em lazy - Os
add_image_sizedo tema cobrem a grelha, o artigo individual e a OG, sempre na mesma proporção - Imagens de destaque com pelo menos 1200 px de largura e
max-image-preview:largedefinido - Banners de consentimento e espaços publicitários têm altura reservada
- CrUX e Search Console verificados de novo depois do deploy
- Nenhum hero de fundo sem contentor com aspect-ratio
- Os fallbacks da grelha de produtos têm a mesma altura de mosaico
Erros típicos de CLS de imagens em projetos WordPress
- Plugin de otimização ativo, mas o tema continua a renderizar tags
<img>fixas - AMP ou fork de «tema móvel» com markup diferente do desktop
- A conversão para WebP substitui os ficheiros, mas não apaga os candidatos antigos do srcset
- A redação insere capturas de ecrã da janela inteira do browser (3000×2000) sem recorte
- O construtor de páginas guarda larguras absolutas em píxeis, que partem no telemóvel e são depois corrigidas por JS
Perguntas frequentes sobre dimensões de imagem e CLS no WordPress
O que causa o CLS provocado por imagens no WordPress
Na maioria dos casos faltam dimensões fixas, variantes responsivas ou espaço reservado no template. Por isso, o layout salta assim que as imagens carregam.
Como corrigir erros de dimensão de imagem de forma limpa
Verifique em conjunto os templates, os tamanhos de imagem, o output do srcset e as regras CSS. Depois volte a medir o CLS em tipos de página reais, e não apenas numa única URL.
Com que frequência se deve verificar o CLS
Depois de alterações ao tema, de novos componentes, de carregamentos maiores de multimédia e de avisos de Core Web Vitals na Search Console.
Width e height partem o layout responsivo
Não. Os atributos HTML fornecem a proporção. Com CSS max-width: 100% e height: auto, a imagem escala e o espaço reservado mantém-se.
Conclusão
Os erros de dimensão de imagem raramente são um problema de um único plugin. Surgem onde os templates contornam a API de anexos, onde o hero e o lazy loading colidem e onde os dados de campo são ignorados. Quem fixar as dimensões, o aspect-ratio e medições reproduzíveis obtém layouts mais estáveis e menos ruído na Search Console, sem cosmética de PageSpeed numa URL de demonstração.
Mais contexto sobre o trabalho de performance: otimização completa do WordPress, como acelerar um site WordPress e o guia de otimização do INP.







