WebP pode reduzir o arquivo, mas não escolhe sozinho quantos pixels o navegador baixa. Se a conversão apenas trocar o formato, uma imagem de 1.200 pixels continuará com 1.200 pixels, mesmo quando aparecer com metade dessa largura na tela.

Este blog encontrou o problema no teste automatizado de desempenho. Em 19 de julho de 2026, a arte do destaque da home passou de um motivo gráfico de cerca de 8 KB para uma imagem de aproximadamente 50 KB. No ambiente de integração contínua, o Largest Contentful Paint (LCP), tempo até a renderização do maior elemento de conteúdo elegível na área visível, chegou a 2.723 ms e ultrapassou o teto de 2.500 ms.

A correção não foi degradar a arte. O gerador passou a produzir versões de 1.200 e 800 pixels, enquanto o HTML ganhou srcset e sizes. Numa comparação local separada, antes e depois no mesmo perfil móvel simulado, o LCP caiu de 2.793 para 2.122 ms. O formato continuou WebP; o navegador é que passou a receber uma alternativa compatível com o espaço ocupado.

O caso original ocorreu com Astro 7.1.1. A implementação atual foi reconferida com Astro 7.2.2 e Node.js 24.15.0; o projeto declara Node.js 22.12.0 como versão mínima. srcset e sizes, entretanto, fazem parte do HTML e não dependem do Astro para funcionar.

WebP muda o formato, não escolhe o tamanho

Converter uma imagem para WebP pode reduzir o arquivo, mas não cria automaticamente versões para diferentes telas. Se o HTML oferece apenas este recurso, o navegador não tem alternativa:

<img
  src="/images/blog/exemplo/card.webp"
  alt=""
  width="1200"
  height="630"
/>

Neste exemplo didático, o CSS pode renderizar a imagem com 360 pixels de largura. O download, porém, continua sendo o arquivo de 1.200 pixels indicado em src.

Formato e resolução resolvem problemas diferentes. WebP comprime cada candidato. A imagem responsiva cria candidatos menores e informa em qual contexto cada um pode ser útil.

A conta que orienta a escolha

Quando usa descritores de largura (w), o atributo srcset apresenta os arquivos disponíveis e a largura natural de cada um. O atributo sizes descreve a largura que a imagem deve ocupar no leiaute, também chamada de espaço de exibição.

<img
  src="/images/blog/exemplo/card.webp"
  srcset="
    /images/blog/exemplo/card-800.webp 800w,
    /images/blog/exemplo/card.webp 1200w
  "
  sizes="(min-width: 768px) 720px, calc(100vw - 2rem)"
  alt=""
  width="1200"
  height="630"
/>

O descritor 800w não redimensiona o arquivo. Ele declara que aquele recurso possui 800 pixels de largura natural. O valor precisa corresponder ao arquivo real.

Com essas informações, o navegador calcula a densidade efetiva de cada candidato: largura do arquivo dividida pela largura prevista em sizes. A escolha também pode considerar densidade da tela, zoom e condições de rede, e o resultado final depende da implementação do navegador. Por isso, srcset oferece opções; não é uma ordem rígida para baixar sempre o menor arquivo.

No caso de 19 de julho, o sizes móvel era 100vw. O perfil usado pelo gate tinha 412 pixels CSS de largura e razão de pixels do dispositivo de 1,75. Para definir a menor variante, o projeto tomou a largura inteira da janela como referência conservadora: 412 × 1,75, cerca de 721 pixels nominais. Uma versão de 720 pixels ficaria ligeiramente abaixo desse alvo. A de 800 pixels ofereceu margem e continuou mais próxima que a de 1.200.

O detalhe importa porque a densidade transforma um cartão aparentemente pequeno em uma necessidade maior de resolução. Medir apenas a largura CSS ignora esse fator.

As variantes de 800 pixels ocupavam 54,6% menos

O gerador de capas deste blog usa Sharp para produzir card.webp, com 1.200 × 630 pixels, e card-800.webp, com 800 × 420. Esta representação abreviada mostra somente a transformação da variante responsiva:

const card800 = await sharp(variantSource)
  .resize(800, 420, { fit: 'cover', position: 'centre' })
  .webp({ quality: 75, effort: 6 })
  .toBuffer();

Antes da inclusão das imagens deste post, em 21 de agosto de 2026, os 17 pares então publicados somavam:

VarianteDimensãoTotal no acervo
card.webp1.200 × 630939.640 bytes
card-800.webp800 × 420426.294 bytes

As variantes de 800 pixels ocupavam, somadas, 54,6% menos bytes. A diferença combina menos pixels com a configuração de compressão da variante, que usa qualidade 75; não é um experimento isolado de resolução. Isso mede os arquivos disponíveis no repositório, não uma economia garantida em toda visita. O ganho de rede só acontece quando o navegador seleciona o candidato menor, e a compressibilidade muda conforme a arte.

O mesmo arquivo ocupa espaços diferentes

Criar duas imagens foi apenas metade da correção. A home usa imagens do mesmo acervo em três tipos de região com larguras diferentes: destaque, grade de posts recentes e arquivo.

RegiãoValor de sizes
Destaque(min-width: 1200px) 1072px, (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)
Grade recente(min-width: 1200px) 520px, (min-width: 960px) calc(50vw - 80px), (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)
Arquivo(min-width: 640px) 208px, min(208px, calc(100vw - 60px))

O srcset pode ser igual nas três regiões. O sizes não, porque ele declara uma estimativa do espaço ocupado por cada componente. Informar 100vw para um cartão que ocupa apenas uma fração da janela faz o navegador superestimar a necessidade e tende a favorecer um arquivo maior.

Em forma equivalente e abreviada, a configuração do destaque ficou assim:

<img
  src={featuredCardImage}
  srcset={cardSrcset(featuredCardImage)}
  sizes="(min-width: 1200px) 1072px, (min-width: 768px) calc(100vw - 128px), (min-width: 640px) calc(100vw - 96px), calc(100vw - 60px)"
  alt=""
  loading="eager"
  fetchpriority="high"
  decoding="async"
  width="1200"
  height="630"
/>

O sizes não controla a largura visual; o CSS continua responsável pelo leiaute. width e height estabelecem a proporção intrínseca e permitem reservar espaço antes do carregamento, o que ajuda a evitar deslocamento de conteúdo.

O texto alternativo está vazio neste caso porque a capa é decorativa e a manchete aparece junto dela em texto real. Uma imagem que comunica informação própria precisa de uma descrição adequada, não de alt="" copiado deste exemplo.

A imagem do LCP não pode esperar

Na home, somente a imagem em destaque recebe loading="eager" e fetchpriority="high". Esses atributos já existiam antes da correção deste caso; não foram eles que recuperaram a métrica. Eles permanecem porque a imagem é a candidata provável a LCP e precisa ser descoberta cedo. As capas abaixo dela usam loading="lazy", pois podem esperar até se aproximarem da área visível.

Aplicar carregamento tardio à imagem de LCP cria atraso desnecessário. Fazer o oposto e marcar todas as capas com prioridade alta também prejudica a hierarquia: se tudo é urgente, a indicação deixa de ajudar o navegador a decidir.

Essa separação completa a solução. srcset e sizes cuidam do recurso adequado; loading e fetchpriority cuidam do momento e da prioridade. O orçamento de Core Web Vitals deste blog continua sendo o gate que confirma se a combinação funcionou.

Quando deixar o Astro gerar as variantes

O Astro pode automatizar boa parte desse trabalho quando a imagem está em src/ e é importada por astro:assets. A propriedade layout gera srcset e sizes:

---
import { Image } from 'astro:assets';
import capa from '../assets/capa.webp';
---

<Image
  src={capa}
  alt="Descrição do conteúdo visual"
  layout="constrained"
  width={1200}
  height={630}
/>

O redimensionamento visual ainda precisa dos estilos responsivos do Astro, habilitados por image.responsiveStyles: true, ou de CSS equivalente no projeto.

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  image: { responsiveStyles: true },
});

As capas deste projeto já chegam processadas em public/images/blog/, por isso o fluxo manual é necessário. O Astro copia arquivos de public/ sem transformação e não cria versões responsivas para eles.

A escolha, portanto, depende da origem da imagem. Arquivo importado de src/ pode usar o serviço do Astro. Se permanecer neste fluxo em public/, o arquivo produzido por um gerador externo precisa chegar com as variantes prontas e receber o HTML correspondente.

Como verificar sem confiar no olho

Uma página pode parecer correta enquanto transfere o arquivo errado. A verificação precisa observar a decisão do navegador.

  1. Execute npm run build e depois npm run preview para abrir a página em um servidor de pré-visualização.
  2. Ative o perfil móvel antes de recarregar, desabilite o cache na aba de rede e faça uma nova carga.
  3. Inspecione a propriedade currentSrc, que mostra qual candidato foi selecionado.
const imagem = document.querySelector('.home-featured img');

({
  arquivo: imagem?.currentSrc,
  larguraExibida: imagem?.getBoundingClientRect().width,
  larguraNatural: imagem?.naturalWidth,
  densidade: window.devicePixelRatio,
});
  1. Confirme na aba de rede o arquivo transferido e seu tamanho. Redimensionar uma aba já carregada pode reaproveitar o candidato maior do cache; por isso o teste começa com o perfil escolhido e uma carga nova.
  2. Rode a medição de desempenho na página que mais concentra imagens. Neste projeto é npm run test:lighthouse, com atenção primeiro à home.

O resultado esperado não é “800 pixels em qualquer celular”. É um candidato coerente com o espaço, a densidade e as condições que o navegador avaliou, sem sacrificar nitidez nem transferir 1.200 pixels por falta de alternativa.

Os erros que fazem WebP continuar pesado

  • Oferecer variantes sem um sizes fiel. O navegador conhece os arquivos, mas recebe uma descrição errada do espaço.
  • Usar um descritor que não corresponde ao arquivo. 800w precisa significar 800 pixels naturais.
  • Aplicar loading="lazy" ao provável LCP. A economia de prioridade vira atraso visível.
  • Marcar todas as imagens como prioridade alta. A página perde a diferenciação entre destaque e conteúdo secundário.
  • Testar apenas redimensionando uma aba com cache. O recurso já escolhido pode mascarar a configuração nova.
  • Copiar 800 e 1.200 como números universais. Os candidatos precisam nascer do leiaute, das densidades relevantes e das imagens do projeto.

As medições de LCP deste caso vieram de um perfil de laboratório fixo, não de uma comparação de usuários reais. Outra home, outra arte ou outra distribuição de dispositivos pode exigir três ou mais larguras. Se a composição da imagem também precisar mudar no celular, o problema passa a ser de direção de arte, normalmente implementada em HTML com <picture>.

WebP pode compactar cada candidato. srcset oferece as alternativas. sizes descreve o espaço. A medição mostra se o navegador recebeu informação suficiente para escolher bem.

Fontes

  • Reprovação do gate em 2.723 ms, reprodução local de 2.793 para 2.122 ms, escolha da variante de 800 pixels e perfil de 412 px com densidade 1,75 — extrato público do registro versionado (registro e código do projeto, nível 1), verificado em 2026-08-21.
  • Versões do caso e da reconferência: Astro 7.1.1, Astro 7.2.2, execução local em Node.js 24.15.0 e requisito Node.js 22.12.0 ou superior — extrato público das versões registradas (código do projeto, nível 1), verificado em 2026-08-21.
  • Geração de card.webp e card-800.webp com Sharp — extrato público da transformação usada pelo gerador (código do projeto, nível 1), verificado em 2026-08-21.
  • Três valores de sizes alinhados ao CSS atual, prioridade do destaque e carregamento tardio das demais capas — extrato público da implementação da home (código do projeto, nível 1), verificado em 2026-08-21.
  • Medição própria dos 17 pares anteriores ao post 18: soma do tamanho em bytes de cada card.webp e card-800.webp, resultando em 939.640 e 426.294 bytes — inventário público com os 17 pares e a fórmula (arquivos do projeto, nível 1), medido em 2026-08-21; valores representam o acervo, não tráfego de usuários.
  • Modelo de srcset e sizes, largura pretendida do espaço e normalização da densidade de candidatos — HTML Living Standard: imagens (padrão oficial, nível 1), verificado em 2026-08-21.
  • Uso de srcset, sizes, largura do dispositivo e densidade da tela na seleção — web.dev: imagens responsivas (documentação oficial, nível 1), verificado em 2026-08-21.
  • Imagens em public/ sem processamento, geração de srcset e sizes com layout e necessidade de estilos responsivos — documentação do comportamento responsivo no Astro e configuração image.responsiveStyles (documentação oficial, nível 1), verificado em 2026-08-21.
  • Recomendação de não aplicar carregamento tardio à imagem de LCP e de limitar fetchpriority="high" às imagens realmente prioritárias — web.dev: otimizar o LCP (documentação oficial, nível 1), verificado em 2026-08-21.
  • Definição formal do LCP como a renderização do maior elemento de conteúdo elegível na área visível — W3C: Largest Contentful Paint (especificação oficial, nível 1), verificado em 2026-08-21.
  • Uso de currentSrc para identificar o recurso selecionado pelo navegador — HTML Living Standard: currentSrc (padrão oficial, nível 1), verificado em 2026-08-21.
  • Teste com perfil de dispositivo, recarga e cache desabilitado — Chrome DevTools: Device Mode e Network panel (documentação oficial, nível 1), verificado em 2026-08-21.
  • Uso de alt="" em imagem decorativa cuja informação já aparece em texto próximo — W3C WAI: Decorative Images (orientação oficial, nível 1), verificado em 2026-08-21.