Figma → Landing Page
Gere uma landing page promocional estática e pronta para deploy a partir de um design do Figma: index.html + css/main.css + js/{tool,pm_main,main}.js + images/*.webp.
Chamada: /figma-landing-page <link-figma> [nome-da-campanha] [diretório-de-saída]
Contrato fixo (não desviar em nenhuma circunstância):
- Exatamente 3 placeholders de modelo:
__PAGE_TITLE__,__FUNCTION_NAME__(ambos em main.js; title também aparece em index.html),__PC_BASE__(em tool.js; 375 = apenas frame H5; 1920 = frames H5+PC). - Os elementos CTA devem obrigatoriamente ter a classe
.join-btn(contrato do seletor fixo do JS), coexistindo com as classes BEM. - Breakpoint de 576px unificado em três lugares: branch rem do JS,
@media (min-width: 576px)no CSS, detecção de plataforma. - Normalização de px: H5
px × 375 ÷ largura-frame-H5 ÷ 100 = rem; PCpx × 1920 ÷ largura-frame-PC ÷ 100 = rem. - Os três arquivos JS do modelo (
tool.jsadaptação rem /pm_main.jsponte postMessage /main.jscamada de negócios da campanha, ordem de carregamento é esta) são fixos e não devem ser alterados em nenhum caractere; a interação da página é inserida apenas sob o marcador// === Page-specific interactions ===emmain.js(dentro da IIFE, pode usar diretamente$p/pm/track/Date.prototype.format). - Regras detalhadas (limites de recorte, nomes de classes, tabela de inferência de interação) em
references/patterns.md, padrão de entrega emreferences/checklist.md— leia ambos os arquivos antes de gerar.
Etapa 1 — Analisar entrada e verificar ambiente
- Extrair
file keyenode-iddo link. Converter hífen em node-id para dois pontos (123-456→123:456) antes de chamar qualquer API do Figma; se a API retornar 404/resultado vazio, verifique esta etapa primeiro. - Três verificações de ambiente (se não passarem, corrija antes de continuar):
- Figma MCP conectado (se não, guie o usuário na autenticação; interações de autenticação não contam como a "única interrupção" da porta de confirmação). Primeiro chame
whoami(isento de cota) para confirmar o tipo de assinatura: assinaturas Viewer/Collab ou Starter têm apenas 6 chamadas MCP/mês — quando o orçamento estiver apertado, pule o MCP e vá diretamente para a rota REST das Etapas 2/4 (PAT armazenado em~/.figma_token; se ausente, peça ao usuário para gerar um em Figma Settings → Security e escrevê-lo em seu terminal para evitar o histórico de chat). which cwebp— se ausente, informe o usuário:brew install webp; se o usuário se recusar a instalar, toda a página retorna para produtos PNG e anota no relatório de entrega.node -e "require('playwright-core')"— se ausente, executenpm i playwright-coreno diretório scratchpad; reutilize o Chrome do sistema (channel: 'chrome'), não baixe binários do navegador.
- Figma MCP conectado (se não, guie o usuário na autenticação; interações de autenticação não contam como a "única interrupção" da porta de confirmação). Primeiro chame
- Diretório de saída padrão:
./output/<slug-do-nome-da-campanha>/(todos os produtos da skill ficam emoutput/; se o nome da campanha não for fornecido, inferir do nome da página/frame do Figma e confirmar na porta de confirmação). Se o diretório já existir e não estiver vazio, não interrompa agora — a confirmação de substituição faz parte do conteúdo da porta de confirmação.
Etapa 2 — Inventariar o design
- Use ferramenta de metadados esparsos (tipo
get_metadata) para ler a árvore de nós; não puxe o contexto de design completo do frame raiz de uma vez. - Quando MCP não estiver disponível/orçamento esgotado: REST
GET /v1/files/<key>/nodes?ids=<node>(sem depth = JSON de subárvore completa, contém texto completo, fontes, valores originais de gradiente de fills, bbox exato — informações mais completas queget_metadata). Armazene localmente e analise repetidamente; uma única chamada cobre todo o percurso; valores de cor/gradiente/texto do CSS vêm diretamente deste JSON, sem necessidade deget_code. - Identificação de frames: largura ≤ 480 → frame H5; ≥ 1200 → frame PC; frames que não podem ser determinados entram na porta de confirmação. Apenas frame H5 →
__PC_BASE__ = 375; frames H5+PC →__PC_BASE__ = 1920. - Produzir lista de ativos: lista de seções (hero, banner, steps, tnc…); cada nó marcado como recorte / texto real / componente interativo (regras de determinação em
references/patterns.md). - Marcar todos os CTAs (Join Now / Registrar / botões do tipo Sign Up) — eles devem receber a classe
.join-btn.
Etapa 3 — Porta de confirmação (única interrupção antes da geração no fluxo normal)
Uma única mensagem lista tudo abaixo, obtém a confirmação do usuário de uma vez; depois disso, não interrompe até a entrega:
- Nome da campanha, valor de
__PAGE_TITLE__, valor de__FUNCTION_NAME__(ex.:WinsdayBillionDropLandingPage) - Diretório de saída (se não estiver vazio, confirme aqui a substituição)
- Lista de recortes (nó →
images/<nome>.webp) - Lista de nós CTA
- Lista de interações: cada item com justificativa de inferência e parâmetros necessários (contagem regressiva → tempo limite/comportamento ao expirar; carrossel → reprodução automática/intervalo; pop-up → momento de disparo; veja tabela em
references/patterns.md). Parâmetros que o design não consegue inferir devem ser preenchidos pelo usuário aqui; nós que não podem ser categorizados marcar como "estático". - Itens de frame com identificação duvidosa
Etapa 4 — Exportar recortes (cadeia de fallback de quatro níveis, degradar conforme disponibilidade)
- MCP
download_assets(quando o orçamento da assinatura for suficiente): máximo de 20 nós por lote, scale 2x, formato png; baixar URLs temporárias com curl. - Renderização REST
GET /v1/images/<key>?ids=…&format=png&scale=2: ids em lote separados por vírgula, mas máximo de 4 nós grandes por lote (se exceder, reportar "Render timeout"); 429 = orçamento de renderização da assinatura esgotado (assinatura Viewer dispara após cerca de 12 imagens, janela de hora); mudar para o próximo nível, não esperar. - Endpoint de imagens originais REST
GET /v1/files/<key>/images: retorna o mapeamento imageRef → URL de imagem original do arquivo inteiro, usa o orçamento do arquivo, quase sem limitação de renderização. Adequado para todos os nós de preenchimento de imagem (fotos/adesivos/ícones — ofills[].imageRefno JSON do nó corresponde para obter resolução original, muitas vezes muito maior que 2x, recortar/diminuir alpha conforme oscaleModedo fill e redimensionar). Não adequado para nós de composição vetorial (painéis/fitas). - Exportação manual pelo usuário (cota zero): peça ao usuário para selecionar o nó no Figma e exportar. Preferir formato SVG — parte vetorial infinitamente nítida, bitmap incorporado em base64 na resolução original (pode extrair com regex
<image>e decodificar, é a melhor fonte para elementos transparentes). Pipeline de rasterização SVG→bitmap 2x: copiar SVG para diretório temporário (nome de arquivo seguro em kebab-case, nomes originais geralmente contêm&/espaços); escrever um HTML wrapper no mesmo diretório<img src="./x.svg">(abrir SVG diretamente com file:// ou referência de arquivo entre diretórios será recusado pelo Chrome); tirar screenshot com playwrightdeviceScaleFactor:2+omitBackground:truepara obter PNG transparente.
- Geral: converter para webp
cwebp -q 80; nomear semanticamente em kebab-case, sem hash; nós puramente gradiente/cor sólida ler diretamente do JSON e escrever CSS, sem gerar imagem. - Atenção: o viewBox do SVG exportado do Figma pode conter sombra/sangria (maior que o bounding box do nó); ao posicionar o contêiner pelo bounding box do nó, deixe a imagem transbordar proporcionalmente, não achatar.
- Falha na exportação de imagem única: tentar 2 vezes → degradar para próximo nível de fallback; pular apenas se toda a cadeia falhar e registrar no relatório de entrega.
Etapa 5 — Gerar código
- Partir de
assets/template/: o conteúdo debase.cssserve como esqueleto inicial decss/main.css(o produto final tem apenas um main.css, não emite base.css separadamente). index.html: substituir<title>; link de fonte trocar pela fonte do design (se não encontrar no Google Fonts, escolher a mais próxima e registrar no relatório); dentro de#app, gerar estrutura semântica por seção, nomes de classe BEM (.hero__bg,.banner__headline), elementos CTA com classe extrajoin-btn.css/main.css: valores retirados das dimensões/cores/gradientes exatos do Figma (a saída HTML+CSS simples doget_codepode ajudar a obter valores), normalizar px primeiro e depois escrever em rem; fundobackground-image, imagens de conteúdo<img>. Em páginas de dois frames: regras H5 primeiro, todas as sobreposições PC dentro de@media (min-width: 576px).js/: copiar os três arquivos do modelo (tool.js / pm_main.js / main.js), substituir 3 placeholders (__PC_BASE__em tool.js, os outros dois em main.js); código de interação confirmado inserido abaixo do marcador em main.js.- Elementos no design que sangram até a borda do frame e são cortados (bbox ultrapassa a largura do frame), o contêiner deve ter
overflow: hiddenpara reproduzir o corte do frame, caso contrário a página transborda horizontalmente. - Autoverificação:
node --check js/*.jsnos três arquivos deve passar.
Etapa 6 — Comparação de screenshot iterativa (por seção, máximo 3 rodadas)
- Iniciar servidor local no diretório de saída:
python3 -m http.server <porta>. - Escrever o seguinte script de screenshot no scratchpad e usá-lo para capturar screenshots por seção (coordenadas de clip obtidas do DOM: dentro de
page.evaluate, para cada elemento de seção, obtergetBoundingClientRect):
// shot.mjs <url> <out.png> <width> <dsf> [clipY clipH]
import { chromium } from 'playwright-core';
const [url, out, width, dsf, clipY, clipH] = process.argv.slice(2);
const browser = await chromium.launch({ channel: 'chrome' });
const page = await browser.newPage({
viewport: { width: +width, height: 800 },
deviceScaleFactor: +dsf,
});
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot(clipY === undefined
? { path: out, fullPage: true }
: { path: out, clip: { x: 0, y: +clipY, width: +width, height: +clipH } });
await browser.close();
- Comparação H5: local width 375, dsf 2; lado do Figma usar
download_assetspara exportar 2x png por nó de seção como imagem de comparação (imagens de comparação vão para scratchpad, não entram emimages/). - Comparar lado a lado por seção, corrigir cada diferença; no máximo 3 rodadas, se ainda houver diferenças, registrar no relatório de entrega.
- Após a convergência da comparação, fazer uma regressão rápida de página inteira com width 414 (dsf 2) para confirmar que não há desalinhamento/transbordamento (checklist exige duas faixas: 375/414).
- Screenshot de frame inteiro apenas para impressão no relatório de entrega, nunca como base para julgamento de diferenças (frame inteiro longo será subamostrado a ponto de não dar para ver tamanho da fonte/diferenças de cor).
- Páginas de dois frames: adicionalmente, com width 1920, dsf 1, repetir a comparação por seção acima para o layout PC (exportar imagem 1x do frame PC); e fazer regressão rápida de página inteira com width 1280 (checklist de dois frames exige 1280/1920).
- Verificação adicional: console do navegador sem erros (
page.on('console')ou DevTools).
Etapa 7 — Entrega
O relatório de entrega contém:
- Árvore do diretório de saída
- Resultados item a item do
references/checklist.md - Diferenças conhecidas restantes (não eliminadas em 3 rodadas)
- Caminhos das imagens de comparação por seção
- Fallbacks utilizados (png em vez de webp, fontes aproximadas, recortes pulados)
Referência rápida de tratamento de erros
| Cenário | Tratamento |
|---|---|
| Figma MCP não conectado/autenticação expirada | Guiar autenticação e continuar a partir do passo interrompido (não conta como interrupção da porta de confirmação) |
| API retorna 404/vazio com node-id | Verificar conversão de hífen para dois pontos |
| Falha ao exportar recorte/429 de limite | Tentar 2 vezes → degradar para próximo nível de fallback (ver Etapa 4) → se ainda falhar, pular + registrar no relatório |
| cwebp ausente e usuário recusa instalação | Produto regride para images/*.png + registrar no relatório |
| Estrutura do frame não reconhecida | Incluir na porta de confirmação e perguntar ao usuário (precisa do node-id específico) |
| Diretório de saída não vazio | Confirmar substituição junto dentro da porta de confirmação |