Communitygithub.com

kyjongSNSoft/landing-page-skill

Use when the user provides a Figma design link/file and wants a promo/marketing landing page generated from it (SNSoft 活动落地页) — produces deploy-ready static HTML + CSS + JS with rem layout, exported cutout images, and the fixed analytics/postMessage runtime. Triggers - /figma-landing-page, "figma 落地页", "把这个 Figma 做成落地页", Figma link + landing page request.

landing-page-skill 是什么?

landing-page-skill is a Claude Code agent skill that use when the user provides a Figma design link/file and wants a promo/marketing landing page generated from it (SNSoft 活动落地页) — produces deploy-ready static HTML + CSS + JS with rem layout, exported cutout images, and the fixed analytics/postMessage runtime. Triggers - /figma-landing-page, "figma 落地页", "把这个 Figma 做成落地页", Figma link + landing page request.

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kyjongSNSoft/landing-page-skill/tree/main/skill

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

预览

来自技能 README

landing-page-skill preview

文档

Figma → Landing Page

Generate a deployable static event landing page from a Figma design: index.html + css/main.css + js/{tool,pm_main,main}.js + images/*.webp.

Invocation: /figma-landing-page <Figma link> [event name] [output directory]

Fixed contract (must not deviate under any circumstances):

  • Template placeholders are exactly 3: __PAGE_TITLE__, __FUNCTION_NAME__ (both in main.js, title also appears in index.html), __PC_BASE__ (in tool.js; 375 = H5 only; 1920 = H5+PC dual frames).
  • CTA elements must additionally carry the .join-btn class (fixed JS selector contract), coexisting with BEM classes.
  • Breakpoint 576px unified in three places: JS rem branch, CSS @media (min-width: 576px), platform determination.
  • px normalization: H5 px × 375 ÷ H5 frame width ÷ 100 = rem; PC px × 1920 ÷ PC frame width ÷ 100 = rem.
  • Template JS trio (tool.js rem adaptation / pm_main.js postMessage bridge / main.js event business layer, loaded in that order) – fixed sections must not be changed; page interactions are only inserted under // === Page-specific interactions === in main.js (inside the IIFE, can directly use $p/pm/track/Date.prototype.format).
  • Detailed rules (cutout boundaries, class names, interaction inference table) are in references/patterns.md, delivery standards in references/checklist.md—read both files before generation.

Step 1 — Parse Input & Environment Check

  1. Extract file key and node-id from the link. Convert hyphens in node-id to colons (123-456 → 123:456) before calling any Figma API; if the API returns 404/empty results, first check this step.
  2. Three environment checks (fix first before proceeding if not passing):
    • Figma MCP connected (if not, guide user to authenticate; authentication interactions do not count as the single interruption in the confirmation gate). First call whoami (exempt from quota) to confirm seat: View/Collab or Starter seats only allow 6 MCP calls/month—when budget is tight, skip MCP and go directly to Step 2/4 REST route (PAT stored in ~/.figma_token, if missing ask user to generate one in Figma Settings → Security, have the user write it to their terminal file to avoid chat history).
    • which cwebp—if missing, tell user brew install webp; if user refuses, fall back to png output for the entire page and note it in the delivery report.
    • node -e "require('playwright-core')"—if missing, npm i playwright-core in the scratchpad directory; reuse the local Chrome browser (channel: 'chrome'), do not download browser binaries.
  3. Output directory defaults to ./output/<event-name-slug>/ (all skill output is placed under output/; if event name is missing, infer from Figma page/frame name and verify in the confirmation gate). If the directory exists and is not empty, do not interrupt now—overwrite confirmation belongs to the confirmation gate.

Step 2 — Survey the Design

  • Use sparse metadata tools (get_metadata class) to read the node tree; do not fetch the full design context of the root frame in one go.
  • When MCP is unavailable/budget exhausted: REST GET /v1/files/<key>/nodes?ids=<node> (without depth = full subtree JSON, containing all text, fonts, fills gradient original values, precise bbox—more complete information than get_metadata). Cache locally and parse repeatedly; one call covers the whole process; CSS color values/gradients/text are taken directly from this JSON, no need for get_code.
  • Frame identification: width ≤ 480 → H5 frame; ≥ 1200 → PC frame; frames that cannot be determined are listed in the confirmation gate. Only H5 frame → __PC_BASE__ = 375; H5+PC dual frames → __PC_BASE__ = 1920.
  • Output asset inventory: section list (hero, banner, steps, tnc…); each node is marked as cutout image / real text / interactive component (determination rules in references/patterns.md).
  • Mark all CTAs (Join Now / Register / Sign Up type buttons)—they must carry .join-btn.

Step 3 — Confirmation Gate (the only interruption before generation in the normal flow)

List all of the following in one message and get user confirmation at once; then no interruptions until delivery:

  • Event name, __PAGE_TITLE__ value, __FUNCTION_NAME__ value (e.g., WinsdayBillionDropLandingPage)
  • Output directory (confirm overwrite if directory is not empty)
  • Cutout image list (node → images/<name>.webp)
  • CTA node list
  • Interaction list: each item includes inference basis and required parameters (countdown→deadline/expiry behavior; carousel→autoplay/interval; popup→trigger timing; see table in references/patterns.md). Parameters that cannot be inferred from the design should be filled in by the user here; nodes that cannot be classified are marked "static".
  • Items with questionable frame identification

Step 4 — Export Cutout Images (four-level fallback chain, degrade based on availability)

  1. MCP download_assets (when seat budget is sufficient): ≤ 20 nodes per batch, scale 2x, format png; download temporary URLs with curl.
  2. REST render GET /v1/images/<key>?ids=…&format=png&scale=2: batch by comma but ≤ 4 large nodes per batch (exceeding causes "Render timeout"); 429 = seat render budget exhausted (View seat triggers after ~12 images, window hourly), switch to next level, do not wait indefinitely.
  3. REST original image endpoint GET /v1/files/<key>/images: returns the full file's imageRef → original image URL mapping, uses file budget, almost not subject to render rate limiting. Suitable for all image fill nodes (photos/stickers/icons—match node JSON's fills[].imageRef to get original resolution, often far exceeding 2x, crop/trim alpha based on fill's scaleMode then scale). Not suitable for vector composite nodes (panels/ribbons).
  4. User manual export (zero quota): ask the user to select and export the node in Figma. Prefer SVG format—vector parts infinitely clear, bitmaps embedded at original resolution as base64 (can regex extract <image> and decode, best source for transparent elements). SVG→2x bitmap rasterization pipeline: copy SVG to a temp directory (kebab-safe filename, original names often contain &/spaces), write an HTML wrapper <img src="./x.svg"> in the same directory (opening SVG directly via file:// or cross-directory file references will be rejected by Chrome), screenshot with playwright deviceScaleFactor:2 + omitBackground:true to get transparent PNG.
  • General: convert to webp cwebp -q 80; name semantically in kebab-case, no hash; pure gradient/solid color nodes read directly from JSON and write CSS, no image output.
  • Note: Figma-exported SVG viewBox may contain drop shadows/bleed (larger than the node box); when the container positions by the node box, let the image overflow proportionally, do not squash.
  • Single image export failure: retry 2 times → degrade to next level chain; skip only if all levels fail and note in delivery report.

Step 5 — Generate Code

  • Start from assets/template/: the content of base.css serves as the initial skeleton of css/main.css (the output has only a single main.css, no separate base.css output).
  • index.html: replace <title>; replace font links with the design's fonts (if Google Fonts cannot match, choose the closest and note in the report); generate semantic structure inside #app by section, BEM class names (.hero__bg, .banner__headline), CTA elements additionally carry join-btn.
  • css/main.css: values taken from Figma precise dimensions/color values/gradients (get_code plain HTML+CSS output can assist in obtaining values), all px-normalized first then written in rem; background background-image, content images <img>. Dual-frame pages: H5 rules first, PC overrides all in @media (min-width: 576px).
  • js/: copy the three template files (tool.js / pm_main.js / main.js), replace the 3 placeholders (__PC_BASE__ in tool.js, the other two in main.js); insert confirmed interaction code below the marker in main.js.
  • Elements that bleed to the frame edge and are cropped (bbox exceeds frame width) should have the container set to overflow: hidden to reproduce frame cropping, otherwise the page will overflow horizontally.
  • Self-check: node --check js/*.js all three files pass.

Step 6 — Screenshot Comparison Iteration (by section blocks, max 3 rounds)

  • Start a local server in the output directory: python3 -m http.server <port>.
  • Write the following screenshot script to scratchpad and use it to screenshot by section blocks (clip coordinates from DOM: use page.evaluate to get getBoundingClientRect for each section element):
// 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();
  • H5 comparison: local width 375, dsf 2; Figma side uses download_assets to export 2x png comparison images by section node (comparison images go to scratchpad, not images/).
  • Compare side-by-side section by section, correct differences; max 3 rounds, remaining differences are noted in the delivery report.
  • After convergence, do a quick full-page regression with width 414 (dsf 2) to confirm no misalignment/overflow (checklist requires 375/414 two profiles).
  • Full-frame screenshots are only for delivery report visuals, never as a basis for difference determination (long pages will be downsampled to illegible font sizes/color differences).
  • Dual-frame pages: additionally repeat the above section-by-section comparison for PC layout with width 1920, dsf 1 (against 1x export of PC frame); and a quick full-page regression with width 1280 (checklist dual-frame profiles require 1280/1920).
  • Check incidentally: browser console has no errors (page.on('console') or DevTools).

Step 7 — Delivery

The delivery report includes:

  1. Output directory tree
  2. references/checklist.md checked item by item
  3. Remaining known differences (not eliminated within 3 rounds)
  4. Section comparison screenshot paths
  5. Fallbacks used (png instead of webp, approximate fonts, skipped cutouts)

Error Handling Quick Reference

ScenarioHandling
Figma MCP not connected/authentication expiredGuide user to authenticate, then continue from the interrupted step (does not count as confirmation gate interruption)
node-id API returns 404/emptyCheck hyphen→colon conversion
Cutout export fails/rate limited 429Retry 2 times → degrade to next level chain (see Step 4) → if still fails, skip and note in report
cwebp missing and user refuses to installFall back to images/*.png + note in report
Frame structure cannot be identifiedList in confirmation gate and ask user (need specific node-id)
Output directory not emptyConfirm overwrite together in the confirmation gate

相关技能