Figma → Landing Page
Genera una página de aterrizaje estática y desplegable a partir de un diseño de Figma: index.html + css/main.css + js/{tool,pm_main,main}.js + images/*.webp.
Comando: /figma-landing-page <enlace Figma> [nombre campaña] [directorio salida]
Contrato fijo (no desviarse bajo ninguna circunstancia):
- Los marcadores de posición de la plantilla son exactamente 3:
__PAGE_TITLE__,__FUNCTION_NAME__(ambos en main.js; title también aparece en index.html),__PC_BASE__(en tool.js; 375 = solo frame H5; 1920 = frames H5+PC). - Los elementos CTA deben tener adicionalmente la clase
.join-btn(contrato de selector fijo de JS), coexistiendo con las clases BEM. - Breakpoint 576px unificado en tres lugares: rama rem de JS,
@media (min-width: 576px)de CSS, y determinación de platform. - Normalización de px: H5
px × 375 ÷ ancho frame H5 ÷ 100 = rem; PCpx × 1920 ÷ ancho frame PC ÷ 100 = rem. - Los tres archivos JS de la plantilla (
tool.jsadaptación rem /pm_main.jspuente postMessage /main.jscapa de negocio de la campaña; el orden de carga es este) tienen segmentos fijos que no se modifican ni una letra; la interacción de la página solo se inserta bajo el comentario// === Page-specific interactions ===demain.js(dentro de la IIFE, puede usar directamente$p/pm/track/Date.prototype.format). - Reglas detalladas (límites de recorte, nombres de clase, tabla de inferencia de interacciones) en
references/patterns.md; estándares de entrega enreferences/checklist.md— léalos antes de generar.
Paso 1 — Analizar entrada y verificar entorno
- Extraer file key y node-id del enlace. Convertir guiones del node-id a dos puntos (
123-456→123:456) antes de llamar a cualquier API de Figma; si la API devuelve 404/resultado vacío, verifique este paso primero. - Tres verificaciones de entorno (si no pasan, reparar antes de continuar):
- Figma MCP conectado (si no, guíe al usuario para autenticarse; la autenticación no cuenta como la única interrupción de la puerta de confirmación). Primero llame a
whoami(excepto del límite) para confirmar el asiento: Asientos View/Collab o Starter solo 6 llamadas MCP/mes — si el presupuesto es ajustado, omita MCP y vaya directamente a la ruta REST del Paso 2/4 (PAT guardado en~/.figma_token; si falta, pida al usuario generar uno en Figma Settings → Security, y que lo escriba en su terminal para evitar que quede en el historial del chat). which cwebp— si falta, informe al usuariobrew install webp; si el usuario se niega a instalarlo, la página completa usará imágenes png y lo indicará en el informe de entrega.node -e "require('playwright-core')"— si falta, instalenpm i playwright-coreen el directorio scratchpad; reutilice Chrome del sistema (channel: 'chrome'), no descargue el binario del navegador.
- Figma MCP conectado (si no, guíe al usuario para autenticarse; la autenticación no cuenta como la única interrupción de la puerta de confirmación). Primero llame a
- Directorio de salida por defecto
./output/<slug-nombre-campaña>/(todos los artefactos del skill se colocan bajooutput/; si no se proporciona nombre de campaña, se infiere del nombre de página/frame de Figma y se verifica en la puerta de confirmación). Si el directorio ya existe y no está vacío, no interrumpa ahora — la confirmación de sobrescritura pertenece al contenido de la puerta de confirmación.
Paso 2 — Inventariar el diseño
- Usar herramientas de metadatos dispersos (como get_metadata) para leer el árbol de nodos; no obtener el contexto de diseño completo del frame raíz de una sola vez.
- Cuando MCP no esté disponible o se haya agotado el presupuesto: REST
GET /v1/files/<key>/nodes?ids=<node>(sin depth = árbol JSON completo, incluye texto completo, fuentes, valores originales de degradados en fills, bbox exacto — información más completa que get_metadata). Guárdelo localmente y analícelo repetidamente; una sola llamada sirve para todo el proceso; los valores de color/degradado/texto en CSS se toman directamente de este JSON, sin necesidad de get_code. - Identificación de frames: ancho ≤ 480 → frame H5; ≥ 1200 → frame PC; los frames no determinables se incluyen en la puerta de confirmación. Solo frame H5 →
__PC_BASE__ = 375; frames H5+PC →__PC_BASE__ = 1920. - Generar lista de activos: lista de secciones (hero, banner, steps, tnc…); cada nodo etiquetado como recorte de imagen / texto real / componente interactivo (reglas de determinación en
references/patterns.md). - Marcar todos los CTA (botones Join Now / Registrarse / Sign Up) — deben llevar la clase
.join-btn.
Paso 3 — Puerta de confirmación (única interrupción antes de generar en el flujo normal)
Una sola lista que contenga todo lo siguiente, obteniendo confirmación del usuario de una vez; no interrumpir de nuevo hasta la entrega:
- Nombre de campaña, valor de
__PAGE_TITLE__, valor de__FUNCTION_NAME__(ej.WinsdayBillionDropLandingPage) - Directorio de salida (si no está vacío, confirmar aquí si se sobrescribe)
- Lista de recortes (nodo →
images/<nombre>.webp) - Lista de nodos CTA
- Lista de interacciones: cada una con su fundamento de inferencia y parámetros necesarios (cuenta atrás→hora límite/comportamiento al vencer; carrusel→reproducción automática/intervalo; ventana emergente→momento de activación; ver tabla en
references/patterns.md). Si el diseño no permite deducir los parámetros, el usuario debe completarlos aquí; los nodos no clasificables se etiquetan como «estáticos». - Elementos dudosos en la identificación de frames
Paso 4 — Exportar recortes (cadena de respaldo de 4 niveles, degradar según disponibilidad)
- MCP
download_assets(cuando el presupuesto de asiento sea suficiente): máx. 20 nodos por lote, scale 2x, format png; descargar URLs temporales con curl. - Renderizado REST
GET /v1/images/<key>?ids=…&format=png&scale=2: ids en lote separados por comas, pero máx. 4 nodos grandes por lote (si se supera, reporta "Render timeout"); 429 = presupuesto de renderizado de asiento agotado (los asientos View lo activan tras unas 12 imágenes, ventana de ~1 hora), pasar al siguiente nivel, no esperar. - Endpoint de imágenes originales REST
GET /v1/files/<key>/images: devuelve el mapeo completo de imageRef → URL de imagen original, usa el presupuesto de archivo, casi sin límite de renderizado. Adecuado para todos los nodos de relleno de imagen (fotos/pegatinas/iconos — elfills[].imageRefdel nodo JSON coincide para obtener resolución original, a menudo muy superior a 2x, recortar según scaleMode de fill y escalar tras recortar alpha). No aplicable a nodos de composición vectorial (paneles/listones). - Exportación manual por el usuario (cuota cero): pida al usuario que seleccione y exporte los nodos en Figma. Preferir formato SVG — las partes vectoriales son infinitamente nítidas, los mapas de bits se incrustan en base64 a resolución original (se puede extraer
<image>con regex, es la mejor fuente para elementos transparentes). Pipeline de rasterización SVG→mapa de bits 2x: copiar el SVG a un directorio temporal (nombre kebab seguro, los nombres originales suelen contener&/espacios), escribir en el mismo directorio un HTML envoltorio<img src="./x.svg">(abrir SVG directamente con file:// o referencias file:// entre directorios serán rechazadas por Chrome), tomar captura con playwrightdeviceScaleFactor:2+omitBackground:truepara obtener PNG transparente.
- General: convertir a webp
cwebp -q 80; nombres semánticos en kebab-case, sin hash; los nodos de degradado puro/color sólido se leen directamente del JSON y se escriben en CSS, no se genera imagen. - Nota: el viewBox de un SVG exportado de Figma puede contener sombra/sangrado (más grande que el cuadro del nodo); al posicionar el contenedor según el cuadro del nodo, dejar que la imagen se desborde proporcionalmente, no aplastar.
- Si falla la exportación de una imagen: reintentar 2 veces → degradar al siguiente nivel de la cadena; solo saltar si fallan todos los niveles y registrarlo en el informe de entrega.
Paso 5 — Generar código
- Partir de
assets/template/: el contenido debase.csscomo esqueleto inicial decss/main.css(el producto final solo tiene un main.css, no se genera base.css por separado). index.html: reemplazar<title>; cambiar el enlace de fuente por la fuente del diseño (si no se encuentra coincidencia en Google Fonts, elegir la más similar y registrarlo en el informe); dentro de#app, generar estructura semántica por sección, nombres de clase BEM (.hero__bg,.banner__headline), los elementos CTA llevan además la clasejoin-btn.css/main.css: valores tomados de las dimensiones/colores/degradados exactos de Figma (la salida HTML+CSS plano de get_code puede ayudar a obtener valores), todo normalizado a rem antes de escribir; fondosbackground-image, imágenes de contenido<img>. Página de doble frame: reglas H5 primero, toda la cobertura PC dentro de@media (min-width: 576px).js/: copiar los tres archivos de la plantilla (tool.js / pm_main.js / main.js), reemplazar los 3 marcadores (__PC_BASE__en tool.js, los otros dos en main.js); el código de interacción confirmado se inserta bajo el marcador en main.js.- Elementos del diseño que sangran hasta el borde del frame y son recortados (bbox supera el ancho del frame): el contenedor debe tener
overflow: hiddenpara reproducir el recorte del frame, de lo contrario la página se desbordará horizontalmente. - Autoverificación:
node --check js/*.jsdebe pasar para los tres archivos.
Paso 6 — Iteración por comparación de capturas (por sección, máximo 3 rondas)
- Iniciar servidor local en el directorio de salida:
python3 -m http.server <puerto>. - Escribir el siguiente script de captura en scratchpad y usarlo para capturar por sección (coordenadas de recorte obtenidas del DOM: dentro de
page.evaluate, para cada sección tomargetBoundingClientRect):
// 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();
- Comparación H5: local width 375, dsf 2; lado Figma usar
download_assetspara exportar 2x png de cada nodo de sección como imagen de comparación (las imágenes de comparación van en scratchpad, no enimages/). - Comparar lado a lado por sección, corregir cada diferencia; máximo 3 rondas; si aún hay diferencias, registrarlas en el informe de entrega.
- Una vez convergida la comparación, hacer una regresión rápida de página completa con width 414 (dsf 2) para confirmar que no hay desbordamientos ni desplazamientos (checklist requiere ambas resoluciones: 375/414).
- La captura de pantalla del frame completo solo se usa para la sensación visual del informe de entrega, nunca como base para determinar diferencias (los frames largos se reducirán a una resolución donde no se vean el tamaño de fuente ni las diferencias de color).
- Página de doble frame: adicionalmente, con width 1920, dsf 1, repetir la comparación por secciones para el diseño PC (contra la imagen 1x exportada del frame PC); y hacer una regresión rápida de página completa con width 1280 (checklist de doble frame requiere 1280/1920).
- Aprovechar para verificar: la consola del navegador no tiene errores (
page.on('console')o herramientas de desarrollador).
Paso 7 — Entrega
El informe de entrega incluye:
- Árbol del directorio de salida
- Resultados de marcar cada elemento de
references/checklist.md - Diferencias conocidas restantes (no resueltas en 3 rondas)
- Rutas de las capturas de comparación por sección
- Medidas de respaldo utilizadas (png en lugar de webp, fuente aproximada, recortes saltados)
Guía rápida de manejo de errores
| Escenario | Manejo |
|---|---|
| Figma MCP no conectado / autenticación fallida | Guiar al usuario para autenticarse y continuar desde el paso interrumpido (no cuenta como interrupción de la puerta de confirmación) |
| node-id llama API y devuelve 404/vacío | Verificar conversión de guiones a dos puntos |
| Fallo de exportación de recorte / límite de velocidad 429 | Reintentar 2 veces → degradar al siguiente nivel (ver Paso 4) → si aún falla, saltar + registrar en informe |
| cwebp faltante y usuario se niega a instalar | Producto final usa images/*.png + registrar en informe |
| Estructura de frame no identificable | Incluir en puerta de confirmación y preguntar al usuario (necesita node-id específico) |
| Directorio de salida no vacío | Confirmar sobrescritura dentro de la puerta de confirmación |