Communitygithub.com

kyjongSNSoft/landing-page-skill

Convierte enlaces o archivos de Figma en páginas de aterrizaje promocionales/marketing listas para desplegar, generando HTML+CSS+JS estáticos con diseño rem, imágenes recortadas exportadas y runtime fijo de analytics/postMessage.

¿Qué es landing-page-skill?

Este skill transforma automáticamente diseños de Figma en páginas de aterrizaje estáticas y listas para producción. Genera una estructura completa: `index.html`, `css/main.css`, `js/{tool,pm_main,main}.js` e `images/*.webp`, siguiendo un contrato fijo de plantillas con tres marcadores de posición (`__PAGE_TITLE__`, `__FUNCTION_NAME__`, `__PC_BASE__`). Utiliza un flujo de trabajo en 7 pasos que incluye: extracción de identificadores de nodos desde enlaces de Figma, inspección del diseño con metadatos dispersos o la API REST de Figma, confirmación interactiva con el usuario (única intervención), exportación de imágenes con una cadena de respaldo de 4 niveles, generación de código con diseño rem adaptable (breakpoint en 576px), comparación por secciones con Playwright (máximo 3 iteraciones) y un informe final de entrega. Está diseñado para agentes como Claude Code, Cursor o Codex y requiere herramientas externas como `cwebp`, Node.js con Playwright, acceso a Figma (MCP o token REST) y un navegador Chrome. El skill prioriza la fidelidad visual y la automatización, manejando errores de forma gradual y registrando todas las desviaciones en el informe.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kyjongSNSoft/landing-page-skill/tree/main/skill

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Vista previa

Del README del skill

landing-page-skill preview

Documentación

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; PC px × 1920 ÷ ancho frame PC ÷ 100 = rem.
  • Los tres archivos JS de la plantilla (tool.js adaptación rem / pm_main.js puente postMessage / main.js capa 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 === de main.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 en references/checklist.md — léalos antes de generar.

Paso 1 — Analizar entrada y verificar entorno

  1. 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.
  2. 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 usuario brew 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, instale npm i playwright-core en el directorio scratchpad; reutilice Chrome del sistema (channel: 'chrome'), no descargue el binario del navegador.
  3. Directorio de salida por defecto ./output/<slug-nombre-campaña>/ (todos los artefactos del skill se colocan bajo output/; 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)

  1. 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.
  2. 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.
  3. 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 — el fills[].imageRef del 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).
  4. 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 playwright deviceScaleFactor:2 + omitBackground:true para 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 de base.css como esqueleto inicial de css/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 clase join-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; fondos background-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: hidden para reproducir el recorte del frame, de lo contrario la página se desbordará horizontalmente.
  • Autoverificación: node --check js/*.js debe 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 tomar getBoundingClientRect):
// 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_assets para exportar 2x png de cada nodo de sección como imagen de comparación (las imágenes de comparación van en scratchpad, no en images/).
  • 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:

  1. Árbol del directorio de salida
  2. Resultados de marcar cada elemento de references/checklist.md
  3. Diferencias conocidas restantes (no resueltas en 3 rondas)
  4. Rutas de las capturas de comparación por sección
  5. Medidas de respaldo utilizadas (png en lugar de webp, fuente aproximada, recortes saltados)

Guía rápida de manejo de errores

EscenarioManejo
Figma MCP no conectado / autenticación fallidaGuiar 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íoVerificar conversión de guiones a dos puntos
Fallo de exportación de recorte / límite de velocidad 429Reintentar 2 veces → degradar al siguiente nivel (ver Paso 4) → si aún falla, saltar + registrar en informe
cwebp faltante y usuario se niega a instalarProducto final usa images/*.png + registrar en informe
Estructura de frame no identificableIncluir en puerta de confirmación y preguntar al usuario (necesita node-id específico)
Directorio de salida no vacíoConfirmar sobrescritura dentro de la puerta de confirmación

Skills relacionados