core-web-vitals · v1.0 · 2026-10-21 · sha256 d60965bae1d49f52
core-web-vitals v1.0A
Immutable. This exact content never changes and is served at /api/v1/blob/d60965bae1d49f52.
--- name: core-web-vitals description: "Diagnosticar e corrigir Core Web Vitals (LCP, INP, CLS) com evidência — campo (CrUX, PageSpeed, Search Console) × laboratório (Lighthouse, trace), subpartes do LCP, fases do INP (long tasks, handlers, hidratação), causas de CLS e correção genérica por framework. Use ao 'melhorar Core Web Vitals', 'corrigir LCP', 'reduzir CLS', 'otimizar INP' ou ao especificar o fix de performance de uma página." version: "1.0" updated: "2026-10-20" --- # Core Web Vitals — medir, achar a causa, corrigir, medir de novo As três métricas de experiência de página do Google: **LCP** (carregamento), **INP** (resposta à interação) e **CLS** (estabilidade visual). Esta skill é o método de diagnóstico e correção. Para o contexto de SEO (peso no ranking, estatísticas do CrUX, mitos como "CWV 2.0") a referência é `seo/references/cwv-thresholds.md` (skill `/seo`) — não repetimos aquilo aqui. ## Limiares | Métrica | Bom | Precisa melhorar | Ruim | |---|---|---|---| | **LCP** | ≤ 2,5 s | 2,5 – 4 s | > 4 s | | **INP** | ≤ 200 ms | 200 – 500 ms | > 500 ms | | **CLS** | ≤ 0,1 | 0,1 – 0,25 | > 0,25 | A régua é o **p75 de campo**: 75% das visitas reais precisam ficar em "bom", por página e por origem, separando mobile e desktop. INP é a única métrica de interatividade — FID foi aposentado em 2024, nunca o cite. ## Campo × laboratório — nunca na mesma frase | Fonte | Tipo | Para quê | |---|---|---| | CrUX (API ou BigQuery), relatório CWV do Search Console, bloco "dados reais" do PageSpeed Insights | **Campo** (usuários reais do Chrome, janela de 28 dias, p75) | Decidir **se** há problema e priorizar | | RUM próprio (biblioteca `web-vitals` com build de atribuição) | **Campo** (seus usuários, por rota, device, release) | Segmentar e atribuir a causa em produção | | Trace do navegador (DevTools → Performance, ou navegador headless) | **Laboratório** (uma carga, condições declaradas) | Diagnosticar **por que** | | Lighthouse / bloco "diagnóstico" do PageSpeed | **Laboratório** (simulado, throttling fixo) | Fallback controlado e comparação antes/depois | | `PerformanceObserver` no console | Sessão única | Depurar uma página aberta | Regras: - **Campo decide, laboratório explica.** Um valor de Lighthouse não é comparável a um p75 de campo — são amostras diferentes. Rotule sempre: "LCP campo p75 mobile 3,4 s (CrUX, 28 dias até DD/MM)" e "LCP lab 2,1 s (Lighthouse mobile, Moto G simulado)". - **Lighthouse não mede INP** (não há interação); o proxy de laboratório é o **TBT** (Total Blocking Time). INP real só vem de campo ou de trace de uma interação gravada. - Página sem volume no CrUX: use o dado de **origem**, rotulado como tal, ou RUM próprio. Nunca invente o p75 de página. - Quando a squad **seo** existe no projeto, o número de campo é da **SESHAT** (`seo-google`), com `#id` — peça a ela em vez de consultar por conta própria. Sem a squad seo, cite a fonte e a data da consulta. - **Sem runtime não há veredicto.** Só com código-fonte dá para apontar causa provável; não afirme que "o LCP está ruim" sem medição. - **Depois do fix:** meça de novo no laboratório nas mesmas condições. Melhora de campo só aparece com novas visitas (CrUX leva até 28 dias) — nunca declare "CWV corrigido" no mesmo dia; declare "lab melhorou de X para Y; campo a confirmar em DD/MM". ## Sequência de trabalho 1. **Campo:** quais métricas reprovam, em qual device, em quais páginas/templates (p75). 2. **Trace** da página reprovada, em condição representativa (mobile, CPU 4× mais lenta, rede 4G) — anote as condições. 3. Leia **só os insights da métrica que reprova** e vá ao código/recurso implicado. 4. Hipótese → correção mínima → **mesmo trace de novo** → compare a subparte/fase, não só o total. 5. Registre: métrica, fonte, subparte dominante, causa, fix, antes/depois de lab, data para conferir o campo. ## LCP — o maior elemento visível O elemento LCP costuma ser imagem de destaque (hero), pôster de vídeo, imagem de fundo via CSS ou um bloco grande de texto. Descubra qual é antes de otimizar — o trace mostra, ou o observer em `references/LCP.md`. **Subpartes** (o total é a soma; ataque a que domina): | Subparte | O que é | Causa típica | Correção típica | |---|---|---|---| | **TTFB** | Até o 1º byte do HTML | Servidor lento, sem cache/CDN, redirects, cold start | Cache na borda, `stale-while-revalidate`, cortar redirects, otimizar a consulta | | **Atraso de carga do recurso** | Do TTFB até o navegador *pedir* o recurso LCP | Imagem descoberta tarde (fundo em CSS, injetada por JS, `loading="lazy"` no hero) | `<img>` no HTML inicial com `fetchpriority="high"`; `preload` só se o trace mostrar descoberta tardia | | **Tempo de carga do recurso** | Download do recurso LCP | Imagem pesada, tamanho errado, formato antigo | Redimensionar, `srcset`/`sizes`, AVIF/WebP, CDN de imagem | | **Atraso de renderização** | Do recurso pronto até a pintura | CSS/JS bloqueando renderização, LCP montado no cliente, fonte bloqueando texto | Cortar CSS bloqueante, `defer` em JS, renderizar no servidor (SSR/SSG/streaming), `font-display: swap` | Nunca `loading="lazy"` no elemento LCP. Não prometa "economia de X ms" genérica: meça a subparte antes e depois. Detalhes, código e Speculation Rules (prerender da próxima navegação) em `references/LCP.md`. ## INP — a pior interação da visita (aprox.) INP mede o tempo de um clique, toque ou tecla até o próximo quadro pintado. Divida em três fases e otimize só a que o trace apontar: | Fase | Evidência | Correção típica | |---|---|---| | **Atraso de entrada** | *Long tasks* (> 50 ms) já ocupando a thread principal quando o usuário interage — script de terceiro, inicialização, **hidratação** | Quebrar tarefas longas, adiar terceiros, reduzir/fatiar a hidratação (ilhas, componentes de servidor, carregar sob demanda) | | **Processamento** | O próprio *event handler* e o trabalho síncrono que ele dispara (re-render grande, cálculo) | Tirar trabalho do handler, dar feedback visual antes, ceder a thread (`scheduler.yield()`), Web Worker para CPU pesada | | **Atraso de apresentação** | Estilo, layout e pintura depois do handler | Reduzir o DOM afetado, evitar layout forçado (ler e escrever geometria intercalados), `content-visibility` em listas longas | Hidratação é causa frequente em sites React/Vue: a página *parece* pronta, mas a thread está ocupada e o primeiro clique espera. Grave o trace **interagindo durante o carregamento**, não só depois. Padrões de código (ceder a thread, feedback antes do trabalho, observer de interações lentas) em `references/INP.md`. ## CLS — nada mexe sem o usuário pedir CLS soma deslocamentos inesperados de layout durante a visita (cada um = fração de impacto × fração de distância). O elemento que *se moveu* costuma ser vítima — procure o que foi **inserido ou redimensionado acima** dele. | Causa | Correção | |---|---| | Imagem, vídeo ou iframe sem dimensão | `width`/`height` no HTML ou `aspect-ratio` no contêiner | | Anúncio, embed, componente assíncrono | Contêiner com altura mínima realista reservada | | Banner de cookies, aviso, mensagem de validação inserido no topo | Sobreposição (overlay) ou espaço reservado antes do conteúdo chegar | | Troca de fonte (fallback → web font com métrica diferente) | Fallback com `size-adjust`/`ascent-override` calculados para o par real de fontes; `font-display: optional` quando couber | | Animação de `top`/`left`/`height`/`width` | Animar `transform` e `opacity` | | Skeleton com altura diferente do conteúdo final | Skeleton com as mesmas dimensões | Teste os estados que mudam layout (scroll, abrir menu, carregar mais, consentimento), não só a carga inicial, e em mais de uma largura. Código e checklist em `references/CLS.md`. ## Correção por framework (genérica) | Problema | Princípio, em qualquer stack | |---|---| | Hero lento | Componente de imagem do framework com prioridade/eager no LCP, `sizes` correto, lazy em todo o resto | | Conteúdo acima da dobra montado no cliente | Renderizar no servidor ou estático; streaming para o resto | | JS demais na carga | Dividir por rota, importar sob demanda o que está abaixo da dobra ou atrás de interação | | Hidratação travando o primeiro clique | Menos componentes interativos no cliente; hidratar sob demanda/visibilidade | | Terceiros (chat, analytics, pixel) | Carregar depois da interação ou da ociosidade; medir o custo de cada um | | Fontes | Auto-hospedar, `preload` só da fonte do LCP, subset, `font-display` | Correções específicas de **React/Next.js** (`next/image` com `priority`, Server Components, `dynamic()`, `useTransition`, Suspense/streaming, `next/font`) estão em `/nextjs-react-best-practices` — use aquela skill, não duplique aqui. ## Quem faz o quê no pack - **seo-performance (SHU)** mede, aponta a subparte/fase e a causa e escreve a especificação do fix; não altera código. - **seo-google (SESHAT)** é a fonte dos números de campo (CrUX/GSC/PSI) quando a squad seo existe. - **sites-dev-delta / dev-dev-delta** implementam a correção e provam com o mesmo trace antes/depois (`/verify-before-done`). ## Checklist de entrega - [ ] Métrica reprovada identificada com fonte de campo, device e data (ou "sem dado de campo" declarado) - [ ] Campo e laboratório rotulados separadamente - [ ] Subparte do LCP / fase do INP / nó e gatilho do CLS identificados no trace - [ ] Uma correção por vez, ligada à causa - [ ] Mesmo trace, mesmas condições, antes e depois - [ ] Data para reler o campo registrada; nenhuma afirmação de melhora de campo antes disso ## Referências - `references/LCP.md` — subpartes, imagens, CSS crítico, SSR/streaming, Speculation Rules, observer - `references/INP.md` — fases, ceder a thread, feedback antes do trabalho, terceiros, observer, atribuição em produção - `references/CLS.md` — reserva de espaço, conteúdo dinâmico, fontes, animação, observer - `/seo` → `references/cwv-thresholds.md` — fatos de SEO sobre CWV (ranking, CrUX, mitos) - web.dev: artigos "LCP", "INP", "CLS" e "Optimize INP" --- Adaptado de addyosmani/web-quality-skills → skills/core-web-vitals (MIT, commit afa8da9) — 2026-10-20; texto em pt-BR, reorganizado para o pack.