Figma → Landing Page
从 Figma 设计稿生成可部署的静态活动落地页:index.html + css/main.css + js/{tool,pm_main,main}.js + images/*.webp。
调用:/figma-landing-page <Figma 链接> [活动名] [输出目录]
固定契约(任何情况下不得偏离):
- 模板占位符恰好 3 个:
__PAGE_TITLE__、__FUNCTION_NAME__(均在 main.js,title 另出现在 index.html)、__PC_BASE__(在 tool.js;375 = 仅 H5 帧;1920 = H5+PC 双帧)。 - CTA 元素必须额外挂
.join-btn(固定 JS 的选择器契约),与 BEM 类并存。 - 断点 576px 三处统一:JS rem 分支、CSS
@media (min-width: 576px)、platform 判定。 - px 归一化:H5
px × 375 ÷ H5帧宽 ÷ 100 = rem;PCpx × 1920 ÷ PC帧宽 ÷ 100 = rem。 - 模板 js 三件(
tool.jsrem 适配 /pm_main.jspostMessage 桥 /main.js活动业务层,加载顺序即此)固定段一个字都不改;页面交互只插在main.js的// === Page-specific interactions ===之下(IIFE 内部,可直接用$p/pm/track/Date.prototype.format)。 - 详细规则(切图边界、类名、交互推断表)见
references/patterns.md,交付标准见references/checklist.md——生成前先读这两个文件。
Step 1 — 解析输入与环境检查
- 从链接提取 file key 与 node-id。node-id 连字符转冒号(
123-456→123:456)后再调任何 Figma API;调 API 得到 404/空结果时第一时间检查这一步。 - 三项环境检查(不通过先修复再继续):
- Figma MCP 已连接(未连接则引导用户认证,认证类交互不计入确认门的「唯一一次打断」)。先调
whoami(豁免限额)确认席位:View/Collab 或 Starter 席位仅 6 次 MCP 调用/月——预算紧张时跳过 MCP,直接走 Step 2/4 的 REST 路线(PAT 存~/.figma_token,缺失时请用户在 Figma Settings → Security 生成,让用户在自己终端写入文件避免入聊天记录)。 which cwebp——缺失则告知用户brew install webp;用户拒绝安装则整页回退 png 产物,并在交付报告注明。node -e "require('playwright-core')"——缺失则在 scratchpad 目录npm i playwright-core;浏览器复用本机 Chrome(channel: 'chrome'),不下载浏览器二进制。
- Figma MCP 已连接(未连接则引导用户认证,认证类交互不计入确认门的「唯一一次打断」)。先调
- 输出目录默认
./output/<活动名-slug>/(skill 全部产物统一放output/下;活动名未提供时从 Figma 页面/帧名推断,于确认门核对)。目录已存在且非空时不要现在打断——覆盖确认属于确认门内容。
Step 2 — 盘点设计稿
- 用稀疏元数据工具(get_metadata 类)读节点树;不要对根帧一次性拉全量 design context。
- MCP 不可用/预算尽时:REST
GET /v1/files/<key>/nodes?ids=<node>(不带 depth = 全子树 JSON,含文本全文、字体、fills 渐变原值、精确 bbox——信息比 get_metadata 更全)。存本地反复解析,一次调用管全程;CSS 色值/渐变/文案直接取自该 JSON,无需 get_code。 - 帧识别:宽 ≤ 480 → H5 帧;≥ 1200 → PC 帧;无法判定的帧列入确认门。仅 H5 帧 →
__PC_BASE__ = 375;H5+PC 双帧 →__PC_BASE__ = 1920。 - 产出资产清单:section 列表(hero、banner、steps、tnc…);每个节点标注 切图 / 真文本 / 交互组件(判定规则见
references/patterns.md)。 - 标出所有 CTA(Join Now / 注册 / Sign Up 类按钮)——它们要挂
.join-btn。
Step 3 — 确认门(正常流程中生成前唯一一次打断)
一条消息列全以下内容,一次拿到用户确认;此后直到交付不再打断:
- 活动名、
__PAGE_TITLE__值、__FUNCTION_NAME__值(如WinsdayBillionDropLandingPage) - 输出目录(目录非空时在此确认是否覆盖)
- 切图清单(节点 →
images/<name>.webp) - CTA 节点清单
- 交互清单:每项附推断依据和所需参数(倒计时→截止时间/到期行为;轮播→自动播放/间隔;弹窗→触发时机;表见
references/patterns.md)。设计稿推不出的参数让用户在此填写;无法归类的节点标注「静态」。 - 帧识别存疑项
Step 4 — 导出切图(四级兜底链路,按可用性降级)
- MCP
download_assets(席位预算充足时):每批 ≤ 20 节点、scale 2x、format png;临时 URL 用 curl 下载。 - REST 渲染
GET /v1/images/<key>?ids=…&format=png&scale=2:ids 逗号批量但每批 ≤ 4 个大节点(超了报 "Render timeout");429 = 席位渲染预算尽(View 席位约 12 张后触发、窗口小时级),换下一级,别死等。 - REST 原图端点
GET /v1/files/<key>/images:返回全文件 imageRef → 原图 URL 映射,走文件预算、几乎不受渲染限流。适合一切图片填充节点(照片/贴纸/图标——节点 JSON 的fills[].imageRef对上号即得原始分辨率,常远超 2x,按 fill 的 scaleMode 裁切/trim alpha 后缩放)。不适用矢量合成节点(面板/缎带)。 - 用户手动导出(零配额):请用户在 Figma 选中节点 Export。首选 SVG 格式——矢量部分无限清晰、位图以原始分辨率 base64 内嵌(可正则抽取
<image>解码,是抽透明元素的最佳来源)。SVG→2x 位图光栅化管线:把 SVG 拷到临时目录(kebab 安全文件名,原名常含&/空格)、写同目录 HTML 包装<img src="./x.svg">(file:// 直开 SVG 或跨目录 file 引用都会被 Chrome 拒载)、playwrightdeviceScaleFactor:2+omitBackground:true截图得透明 PNG。
- 通用:转 webp
cwebp -q 80;命名语义化 kebab-case,不带 hash;纯渐变/纯色节点直接读 JSON 写 CSS,不出图。 - 注意:Figma 导出的 SVG viewBox 可能含投影/出血(比节点框大),容器按节点框定位时让图等比溢出,勿压扁。
- 单图导出失败:重试 2 次 → 降级下一链路;全链路失败才跳过并记入交付报告。
Step 5 — 生成代码
- 从
assets/template/起步:base.css内容作为css/main.css的开头骨架(产物只有单个 main.css,不单独输出 base.css)。 index.html:替换<title>;字体 link 换成设计稿字体(Google Fonts 匹配不到时选最近似并记入报告);#app内按 section 生成语义化结构,BEM 类名(.hero__bg、.banner__headline),CTA 元素额外挂join-btn。css/main.css:数值取自 Figma 精确尺寸/色值/渐变(get_code 的 plain HTML+CSS 输出可辅助取值),全部先做 px 归一化再写 rem;背景background-image,内容图<img>。双帧页:H5 规则在前,PC 覆盖全部放@media (min-width: 576px)。js/:拷贝模板三件(tool.js / pm_main.js / main.js),替换 3 个占位符(tool.js 的__PC_BASE__、main.js 的另外两个);确认过的交互代码插在 main.js 的 marker 之下。- 设计里出血到帧边缘被裁的元素(bbox 超出帧宽),容器要
overflow: hidden复现帧裁切,否则页面横向溢出。 - 自检:
node --check js/*.js三件全部通过。
Step 6 — 截图对比迭代(按 section 分块,上限 3 轮)
- 在输出目录起本地服务:
python3 -m http.server <端口>。 - 把以下截图脚本写到 scratchpad 并用它按 section 分块截图(clip 坐标从 DOM 拿:
page.evaluate里对每个 section 元素取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();
- H5 对比:本地 width 375、dsf 2;Figma 侧用
download_assets按 section 节点导出 2x png 对比图(对比图放 scratchpad,不进images/)。 - 逐 section 并排对比、逐差异修正;最多 3 轮,仍有差异则记入交付报告。
- 对比收敛后,另以 width 414(dsf 2)整页快速回归一次,确认无错位溢出(checklist 要求 375/414 两档)。
- 整帧截图只用于交付报告观感,绝不作为差异判定依据(长页整帧会被降采样到看不清字号/色差)。
- 双帧页:另以 width 1920、dsf 1 对 PC 布局重复以上分块对比(对 PC 帧 1x 导出图);并以 width 1280 整页快速回归(checklist 双帧档要求 1280/1920)。
- 顺手检查:浏览器 console 无报错(
page.on('console')或开发者工具)。
Step 7 — 交付
交付报告包含:
- 输出目录树
references/checklist.md逐项勾选结果- 剩余已知差异(3 轮内未消除的)
- 分块对比截图路径
- 用过的兜底(png 代 webp、近似字体、跳过的切图)
错误处理速查
| 场景 | 处理 |
|---|---|
| Figma MCP 未连接/认证失效 | 引导认证后从中断步骤继续(不计入确认门打断) |
| node-id 调 API 404/空 | 检查连字符→冒号转换 |
| 切图导出失败/限流 429 | 重试 2 次 → 降级下一级链路(见 Step 4)→ 仍失败跳过 + 记报告 |
| cwebp 缺失且用户拒装 | 产物回退 images/*.png + 记报告 |
| 帧结构无法识别 | 列入确认门问用户(要具体 node-id) |
| 输出目录非空 | 确认门内一并确认覆盖 |