Communitygithub.com

bitovi/ai-enablement-prompts

Compara uma URL de base com uma URL de desenvolvimento ou Storybook, capturando screenshots do Playwright em múltiplos breakpoints, realizando diff de pixels e reportando resultados para guiar correções de estilo e HTML. Use ao replicar uma página ou componente existente, verificar precisão visual ou checar fidelidade responsiva.

O que é ai-enablement-prompts?

Esta skill, chamada Visual Diff, permite que agentes de IA comparem visualmente duas URLs — uma linha de base (ex.: site de produção) e uma atual (ex.: servidor de desenvolvimento local ou história do Storybook) — para identificar diferenças no nível de pixel e orientar correções de HTML/CSS. Ela automatiza todo o fluxo: captura screenshots com o Playwright em breakpoints customizáveis (mobile, tablet, desktop), executa um diff pixel a pixel usando pixelmatch, e gera imagens de diferença com destaque em vermelho (diferenças estruturais) e amarelo (anti-aliasing). A skill é ideal para desenvolvedores que estão replicando páginas ou componentes no Storybook, verificando fidelidade visual após mudanças de estilo, ou garantindo consistência responsiva. Funciona com o MCP do Playwright (requer a flag `--caps=vision` para que o agente enxergue as imagens) e depende dos pacotes `pixelmatch` e `pngjs`. O agente também pode interpretar os resultados quantitativos (percentual de pixels diferentes) e a inspeção visual da imagem de diff para sugerir correções específicas. Plataformas suportadas: VS Code (via MCP), Claude Code, Cursor, Codex e qualquer ambiente que suporte MCP do Playwright. O workflow é iterativo: captura, diff, análise, correção e re-diff até que o percentual de diferença atinja um nível aceitável.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/bitovi/ai-enablement-prompts/tree/HEAD/plugins/playwright/skills/visual-diff

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Skill: Diff Visual

Compara duas URLs visualmente — uma linha de base (ex.: site de produção) e uma atual (ex.: servidor de desenvolvimento local ou história do Storybook) — para encontrar diferenças no nível de pixel e orientar correções.


Quando Usar

  • Replicar uma página ou seção existente no Storybook ou no site Astro
  • Verificar a precisão visual de um componente em relação a um design de referência
  • Checar a fidelidade responsiva em diferentes breakpoints após alterações de estilo
  • Iterar no CSS/HTML até que um componente corresponda à sua referência

Quando NÃO Usar

  • Alterações apenas de conteúdo (atualizações de texto) onde o layout não muda
  • Comparar designs do Figma — use a skill Figma MCP
  • Auditorias de desempenho ou acessibilidade — use Lighthouse ou o addon de a11y

Pré-requisitos

Antes de começar, confirme:

  1. Playwright MCP está configurado em .vscode/mcp.json com --caps=vision
  2. pixelmatch e pngjs estão instalados: verifique as devDependencies no package.json
  3. A URL de linha de base está acessível (site externo ou servidor rodando)
  4. A URL atual está acessível (servidor de desenvolvimento na porta :4321, Storybook na porta :6006, etc.)
  5. O diretório temp/ existe na raiz do workspace (está no .gitignore)

Se o servidor de desenvolvimento ou o Storybook não estiver rodando, inicie-os usando as tarefas do VS Code:

  • Servidor de desenvolvimento: use a tarefa Dev Server
  • Storybook: use a tarefa Storybook

Fluxo de Trabalho

Passo 1: Receber entradas

O agente precisa de:

EntradaObrigatórioExemplo
URL de linha de baseSimhttps://www.bitovi.com/
URL atualSimhttp://localhost:4321/ ou http://localhost:6006/?path=/story/card--default
Seletor CSSNão#hero-section ou .card-grid (para comparação escopada a elemento)
BreakpointsNãoPadrão: mobile (375×667), tablet (768×1024), desktop (1280×720)

Passo 2: Screenshot da linha de base

Comparação escopada a componente (preferida ao comparar um único componente)

Ao comparar um componente específico — especialmente quando a história do Storybook renderiza apenas aquele componente, mas a página de linha de base tem a página completa ao redor — corte para a bounding box do componente para que o diff não seja poluído por seções não relacionadas da página (barras de navegação, overlays fixos, outros módulos acima/abaixo).

Como cortar na página de linha de base:

  1. Navegue até a URL de linha de base
  2. Redimensione a viewport para { width: 1280, height: 720 }
  3. Role o elemento alvo para a visualização:
    () => document.querySelector('<selector>').scrollIntoView({ behavior: 'instant', block: 'start' })
    
  4. Meça a altura renderizada do componente:
    () => document.querySelector('<selector>').getBoundingClientRect().height
    
  5. Redimensione a altura da viewport para corresponder exatamente a essa altura (mantendo a largura constante):
    mcp_playwright_browser_resize → { width: 1280, height: <altura medida> }
    
  6. Capture o screenshot — agora ele captura apenas a área do componente.

Aviso de sangria de fundo: O que está visível atrás do componente será diferente entre o Storybook (fundo em branco) e a página de linha de base (outros módulos). Casos comuns: barras de navegação sticky/locais sobrepondo o topo, modais/overlays com conteúdo de página visível atrás e componentes parcialmente transparentes. Esses diffs residuais se agrupam fora do conteúdo do próprio componente e não são acionáveis — ignore-os.

Como cortar na história do Storybook:

O iframe.html do Storybook renderiza apenas o componente sem nenhum chrome ao redor — não precisa redimensionar. Apenas navegue e capture o screenshot:

mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=<story-id>&viewMode=story" }
mcp_playwright_browser_resize → { width: 1280, height: <mesma altura do corte da linha de base> }
mcp_playwright_browser_take_screenshot → { filename: "temp/vdiff-current-desktop.png" }

Screenshot de viewport completa (ao comparar páginas inteiras)

1. mcp_playwright_browser_navigate → { url: "<baseline-url>" }
2. mcp_playwright_browser_resize → { width: 1280, height: 720 }
3. mcp_playwright_browser_wait_for → { time: 2 }       # deixe a página renderizar completamente
4. mcp_playwright_browser_take_screenshot → {
     type: "png",
     filename: "temp/vdiff-baseline-desktop.png"
   }

Repita para cada breakpoint:

BreakpointLarguraAlturaSufixo do arquivo
Mobile375667-mobile
Tablet7681024-tablet
Desktop1280720-desktop

Passo 3: Screenshot da URL atual

Repita exatamente o mesmo processo para a URL atual, usando nomes de arquivo temp/vdiff-current-{breakpoint}.png.

Importante: Use as mesmas dimensões de viewport e a mesma estratégia de corte (escopada a componente ou página completa) em ambos os lados para que a comparação seja justa.

Passo 4: Executar o script de diff

Para cada breakpoint, execute o script de diff visual no terminal:

node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-desktop.png \
  --current temp/vdiff-current-desktop.png \
  --output temp/vdiff-diff-desktop.png

O script gera JSON na saída padrão:

{
  "totalPixels": 921600,
  "diffPixels": 4521,
  "diffPercent": 0.49,
  "width": 1280,
  "height": 720,
  "baselineDimensions": { "width": 1280, "height": 720 },
  "currentDimensions": { "width": 1280, "height": 720 },
  "dimensionsMismatch": false,
  "diffImagePath": "/absolute/path/to/temp/vdiff-diff-desktop.png"
}

Se dimensionsMismatch for true, as imagens tinham tamanhos diferentes. O script preenche a imagem menor com pixels transparentes antes de comparar. Isso geralmente indica um problema de layout que merece investigação.

Passo 5: Visualizar a imagem de diff

Inicie um servidor HTTP temporário para servir o diretório temp/ e, em seguida, navegue com o Playwright até a imagem de diff:

# Inicie o servidor em segundo plano (execute uma vez por sessão)
npx -y http-server temp/ -p 8787 --cors -c-1 &

Depois, navegue com o Playwright até a imagem de diff:

mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-desktop.png" }
mcp_playwright_browser_take_screenshot → {}   # aciona a visão — o agente agora pode ver o diff

A flag --caps=vision no Playwright MCP significa que o agente pode ver a imagem diretamente na saída do screenshot.

Nota: URLs file:// são bloqueadas pelo Playwright MCP, então o servidor HTTP é necessário. Inicie-o uma vez e reutilize-o para todas as imagens de diff na sessão.

Interpretando a imagem de diff:

  • Pixels vermelhos = diferenças entre linha de base e atual
  • Pixels amarelos = diferenças de anti-aliasing (geralmente ignoráveis)
  • Original escurecido = áreas que correspondem (mostradas com 30% de opacidade para contexto)
  • Grandes aglomerados vermelhos = problemas estruturais (elementos faltando, mudanças de layout, tamanhos errados)
  • Pontos vermelhos espalhados = renderização sub-pixel, suavização de fonte ou anti-aliasing (geralmente aceitável)
  • Bordas vermelhas = incompatibilidade de dimensão ou diferenças de padding/margem

Passo 6: Interpretar e agir

Combine os dados quantitativos do JSON com a inspeção visual do diff:

diffPercentInterpretaçãoAção
< 1%Visualmente idênticoApenas diferenças sub-pixel menores. Nenhuma ação necessária.
1–5%Correspondência próximaInspecione a imagem de diff em busca de diferenças de espaçamento, peso da fonte ou borda. Pequenos ajustes de CSS provavelmente necessários.
5–15%Diferenças notáveisMudanças de layout, incompatibilidades de cor ou elementos faltando. Revise os aglomerados vermelhos no diff para identificar quais seções precisam de trabalho.
> 15%Lacuna significativaGrandes diferenças estruturais ou de estilo. Foco nas maiores regiões vermelhas primeiro — elas indicam as maiores discrepâncias de layout.

Ao interpretar a imagem de diff, observe:

  • Onde na página estão os aglomerados vermelhos? (topo, meio, fundo, esquerda, direita)
  • Eles estão associados a elementos específicos? (navegação, hero, cards, rodapé)
  • Os aglomerados sugerem problemas de espaçamento, diferenças de cor ou conteúdo faltando?

Passo 7: Corrigir e re-diff

  1. Faça correções de CSS/HTML com base na análise do diff
  2. Repita os Passos 3–6 (apenas re-faça o screenshot da URL atual)
  3. Continue até que diffPercent atinja um nível aceitável

Alvos típicos:

  • Replicação exata: < 2%
  • Correspondência razoável: < 5%
  • Correspondência estrutural (conteúdo diferente): < 15%

Screenshots de página completa

Por padrão, os screenshots capturam apenas a viewport visível. Para páginas longas, use a opção fullPage:

mcp_playwright_browser_take_screenshot → {
  type: "png",
  fullPage: true,
  filename: "temp/vdiff-baseline-desktop-full.png"
}

Aviso: Screenshots de página completa produzem imagens grandes e diffs mais lentos. Use apenas ao comparar layouts de página inteira.


Dicas específicas para Storybook

Ao capturar screenshots de histórias do Storybook:

  1. Use a URL iframe para screenshots mais limpos (sem chrome do Storybook):

    http://localhost:6006/iframe.html?id=components-card--default&viewMode=story
    
  2. Aguarde a renderização — as histórias do Storybook podem levar um momento para hidratar:

    mcp_playwright_browser_wait_for → { time: 3 }
    
  3. Screenshots de elemento funcionam bem para isolar o conteúdo da história de qualquer padding do Storybook.


Ajuste de limite

A flag --threshold (0 a 1) controla a sensibilidade do pixelmatch:

ValorSensibilidadeCaso de uso
0.05Muito rigorosoCorrespondência exata de pixels, captura tudo
0.1PadrãoBom equilíbrio, ignora a maioria do anti-aliasing
0.2PermissivoTolerante a diferenças de renderização de fonte entre sistemas
0.3Muito permissivoApenas captura grandes diferenças de cor/layout
node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-desktop.png \
  --current temp/vdiff-current-desktop.png \
  --output temp/vdiff-diff-desktop.png \
  --threshold 0.2

Exemplo: Comparando uma seção hero

# 1. Screenshot da linha de base (hero do site de produção)
mcp_playwright_browser_navigate → { url: "https://www.bitovi.com/" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 2 }
mcp_playwright_browser_snapshot → {}
# Encontre a referência para a seção hero, ex.: ref="hero-[1]"
mcp_playwright_browser_take_screenshot → {
  type: "png",
  element: "hero section",
  ref: "hero-[1]",
  filename: "temp/vdiff-baseline-hero-desktop.png"
}

# 2. Screenshot da atual (história do Storybook)
mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=components-hero--default" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 3 }
mcp_playwright_browser_take_screenshot → {
  type: "png",
  filename: "temp/vdiff-current-hero-desktop.png"
}

# 3. Execute o diff (no terminal)
node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-hero-desktop.png \
  --current temp/vdiff-current-hero-desktop.png \
  --output temp/vdiff-diff-hero-desktop.png

# 4. Inicie o servidor temporário (uma vez por sessão, em terminal em segundo plano)
npx -y http-server temp/ -p 8787 --cors -c-1 &

# 5. Visualize a imagem de diff via Playwright
mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-hero-desktop.png" }
mcp_playwright_browser_take_screenshot → {}   # aciona a visão

# 6. Interprete: leia a saída JSON + inspecione visualmente o diff
# 7. Corrija os estilos, re-faça o screenshot da atual, re-diff

Limpeza

Todos os arquivos de saída vão para temp/, que está no .gitignore. Para limpar após uma sessão:

rm -f temp/vdiff-*.png

Habilidades Relacionadas