| name | figma-to-page |
| description | 按 Figma 设计稿还原页面代码,拉结构化设计数据 + design token,写完用像素 diff 自校验迭代到还原。当用户给出 figma.com 链接要实现页面、或说「照设计稿写这个页面 / 还原设计图 / 这个页面和设计稿对不上 / 按 Figma 做 UI / 设计稿还原度不够」时触发。 |
figma-to-page — 设计稿一次还原
核心判断:还原度差不是「模型不会写样式」,而是「输入信息量不足 + 没有闭环校验」。
本 skill 只做静态视觉还原,不做交互与动效。
何时触发
用户说:
- 「照这个 Figma 稿把页面写出来」/ 给出
figma.com/design/...?node-id=... 链接
- 「还原一下这个设计图」/「按设计稿做这个页面」
- 「做出来跟设计稿对不上 / 还原度不够 / 差挺多」
四条铁律
- 不看截图猜数值。 间距、字号、行高、色值一律从
get_design_context / get_variable_defs 取真值。截图只用于最后对答案。
- 不硬编码样式值。 所有色值 / 字号 / 间距必须走项目的 token / theme 层;项目没有就先建一个(阶段 2)。
- 不靠肉眼说「差不多」收工。 必须跑
bin/pixdiff.py 拿到量化偏差,收敛到阈值或如实报告残留。
- 取数策略要匹配实际配额,不要凭空节省。 配额充裕就走完整模式(更准),配额紧张才省调用 —— 由
bin/quota-mode.sh 决定,见下。
MCP 取数策略
Figma 读取类工具的配额按 plan + seat 计,上下限差 100 倍:
| Seat | Starter | Professional | Organization | Enterprise |
|---|
| View / Collab | 20 /月 | 6 /月 | 6 /月 | 6 /月 |
| Dev / Full | 20 /月 | 200 /天 | 200 /天 | 600 /天 |
豁免(不计配额):whoami、generate_figma_design、add_code_connect_map。
所以不存在一个「对所有人都对」的默认值。 省调用模式牺牲还原精度换配额(跳过 get_metadata、不分区块拉 context,大 frame 会糊);配额够的人走它纯属白亏。用脚本决定:
bash <skill>/bin/quota-mode.sh
| 配置值 | 行为 |
|---|
unlimited | 走完整模式,且不必向用户播报预计调用次数 |
thrifty | 走省调用模式 |
auto(默认) | 输出 MODE=auto → 调 whoami(免费)拿 seat/tier,再跑 quota-mode.sh resolve <seat> <tier> 得到终态 |
优先级:环境变量 FIGMA_QUOTA_MODE > ~/.config/figma-to-page/quota.conf > auto。
用户说自己不受配额约束时,给他:bash <skill>/bin/quota-mode.sh set unlimited(写一次,以后所有会话生效)。
无论哪种模式都成立的两条:
- 文件级数据必须缓存复用 ——
get_variable_defs、get_code_connect_map 是整个设计文件的属性,不随页面变化,用 bin/figma-cache.sh 存盘,同一文件只付一次。这不只省配额,也省上下文和往返时间。
- 撞到 rate limit 报错就立刻降级:切省调用模式跑完当前帧,并告诉用户「实测已触限,建议
quota-mode.sh set thrifty」。不要在同一个模式下反复重试。
REPORT_QUOTA=yes 时,开工前把预计消耗告诉用户(例:「本次预计 3 次调用,其中 token 定义走缓存」);no 时跳过这句,别制造噪音。
阶段 0 · 预检
bash <skill>/bin/detect-stack.sh <项目根目录>
bash <skill>/bin/figma-cache.sh list
bash <skill>/bin/quota-mode.sh
quota-mode.sh 输出 MODE=auto 时(默认),再调 whoami(不计配额)拿 seat 与 tier,喂给 quota-mode.sh resolve <seat> <tier> 得到终态。输出 full / thrifty 时说明用户已显式配置,不用再调 whoami。
输出技术栈、token 层候选位置、可用截图手段、Pillow 是否可用。把这份能力报告告诉用户,特别是需要降级的部分(没有 token 层 / 没有自动截图手段 / 缺 Pillow)。REPORT_QUOTA=yes 时一并给出本次预计的 MCP 调用次数。不要静默跳过任何一项。
同时确认链接是否带 node-id。没有 node-id 就是整个文件而非具体 frame —— 要求用户在 Figma 里选中目标 frame 再复制链接。链接里 figma.com/design/<fileKey>/<名字>?node-id=1-2 → fileKey 与 nodeId=1:2。
别在没有 node-id 的情况下猜一个 —— 工具会拒绝,白烧一次配额。
阶段 1 · 抽取设计真值
Figma MCP 工具名的前缀随客户端而变(如 mcp__claude_ai_Figma__get_design_context),按实际可用的工具名调用。
按阶段 0 定下的 MODE 选一套。
完整模式(MODE=full)
| 步骤 | 工具 | 说明 |
|---|
| 1.1 | get_metadata | 先拿轻量节点索引,看清 frame 的结构分区 |
| 1.2 | get_design_context | 按 1.1 的结构分区块多次调用,而不是整个 frame 一把梭 |
| 1.3 | get_screenshot | 基准图,详见下方「截图落盘」 |
| 1.4 | download_assets | 仅当 frame 里确有图标/图片时调用 |
| 1.5 | get_variable_defs | 先查缓存,未命中才调,调完立刻写缓存 |
| 1.6 | get_code_connect_map | 同上。项目没配 Code Connect 就永久跳过 |
分区块比整拉更准 —— 大 frame 整拉会因为响应被截断而丢深层节点的真值,而且更省上下文(只带回你要写的那块)。代价是更多次调用,配额够的时候这笔买卖划算。
省调用模式(MODE=thrifty)
每帧 2–3 次调用。 首次接触某个设计文件时额外 +2(之后走缓存)。
| 步骤 | 工具 | 计费 | 说明 |
|---|
| 1.1 | get_design_context | 1 | 直接拉整个 frame,不先 get_metadata。拿 layout / 文本 / 填充 / 圆角 / 描边真值 |
| 1.2 | get_screenshot | 1 | 基准图,详见下方「截图落盘」 |
| 1.3 | download_assets | 0–1 | 仅当 frame 里确有图标/图片时调用;纯文字卡片布局跳过 |
| 1.4 | get_variable_defs | 0–1 | 先查缓存,未命中才调,调完立刻写缓存 |
| 1.5 | get_code_connect_map | 0–1 | 同上。项目没配 Code Connect 就永久跳过 |
约 6–8 个页面/月(20 次配额)。整拉大 frame 有丢真值的风险,阶段 4 的像素 diff 会把它暴露出来 —— 到时按缺陷簇针对性补一次分区 get_design_context,不要盲目重拉。
缓存用法(两种模式都要做)
bash <skill>/bin/figma-cache.sh get <fileKey> variables && echo 命中
bash <skill>/bin/figma-cache.sh put <fileKey> variables <结果文件>
截图落盘(阶段 4 的闭环依赖这步)
get_screenshot 默认返回短时效 URL + curl 指令,不是内联图片。照它给的 curl 存到 scratchpad:
curl -sL "<返回的URL>" -o <scratchpad>/design-baseline.png
三条硬性要求:
- 不要设
enableBase64Response: true。 内联 base64 会吃掉大量上下文,而且 pixdiff.py 需要的是磁盘文件路径。只有在完全没有 shell 的环境才用它。
- 显式设
maxDimension。 默认值 1024 会把长边压到 1024,桌面端稿子(如 1440 宽)会被降采样,拿它当像素比对基准就失准了。响应的 JSON 里有 original_width / original_height,按节点原始尺寸设置;若发现返回的 width/height 小于 original,按 original 重取一次。
- URL 是短时效的,立刻 curl。
download_assets 返回的资源 URL 同理(且 raw images 与 svg 各上限 20 个)。
阶段 2 · token 对账
- 把 1.2 拿到的 Figma 变量与阶段 0 定位的项目 token 层逐项比对,产出
token-map.md(scratchpad 里),三列:Figma 变量名 → 项目 token → 状态(已有 / 新增)。
- 缺失的 token 先补进 theme 层,再写页面。
- 项目根本没有 token 层时:主动建一个(Flutter →
AppColors/AppTextStyles/AppSpacing + ThemeData;Web → CSS 变量或 tailwind theme)。建之前告诉用户你要新建哪些文件。
- 命名跟随 Figma 变量语义,不要按具体色值命名(
AppColors.textSecondary 而非 AppColors.gray666)。
这一步的产出是地基。跳过它,后面每个页面都会重新长出一套硬编码值。
阶段 3 · 生成代码
写代码前读映射表(只在这一步读,不要提前载入):
<skill>/references/layout-mapping.md
硬性要求:
- Auto Layout 译成布局语义,不是绝对定位。
space-between → MainAxisAlignment.spaceBetween / justify-between,不是算出 137px 的 SizedBox。绝对定位只在 Figma 里确实是非 Auto Layout 容器时才用。
- 行高必须换算。 Figma 的
lineHeight 有 PIXELS / PERCENT / AUTO 三种单位,Flutter 的 TextStyle.height 是倍数。这是最高频的隐形偏差来源,细则见映射表。
- HUG / FILL / FIXED 三种 sizing 分别对应不同写法,不要一律给死宽高。
- 遵循项目既有的组件与代码风格;1.3 命中的组件直接复用。
阶段 4 · 校验闭环(核心)
这一步决定「一次还原」能不能成立。不允许跳过。
python3 <skill>/bin/pixdiff.py \
--design <阶段1.6的基准图> \
--actual <实现截图> \
--threshold 1.0 \
--out-dir <scratchpad>/diffout
脚本会输出:整体偏差百分比 + 按缺陷聚合并排序的待修清单 + 三张图(热力图 / 红框标注图 / 并排图)。
迭代规则:
- 读
diff-annotated.png 和待修清单,从 #1 开始修。一个簇是一个缺陷,不要把同一簇当成多个问题。
- 改完重新截图、重跑脚本。
- 收敛判定(满足任一即停):
- 偏差 ≤ 1%(退出码 0)→ 通过
- 连续两轮偏差无改善 → 停止,如实报告残留偏差
- 达到 5 轮 → 同上
- 绝不假装收敛。 没到阈值就明说当前偏差是多少、卡在哪。
截图前先确认比对条件一致:同一设备尺寸、同一滚动位置、同样有无状态栏。脚本报「长宽比不一致」时,先修截图范围,再看偏差数字 —— 此时的数字没有意义。
缺 Pillow 时降级:把基准图与实现截图并排交给自己做肉眼对比,列出可测量偏差(padding 差几 px、字号差几号、色值差一档)。必须告知用户这一轮没有量化阈值。
阶段 5 · 收尾
报告三件事:
- 最终还原度(偏差百分比)与是否达标
- 残留偏差清单(如有)—— 具体位置 + 原因 + 建议
- 交互 / 动效 / 响应式 TODO(本 skill 不做的部分)
然后提示用户 /clear:图片一旦读入即常驻整轮上下文,一个会话连做多个 frame 会明显变慢变贵。
上下文纪律
- 一轮只做一个 frame。 多个页面分多轮,每轮之间
/clear。
- 同一张图只读一次,读过就记住,不要回头重读。
- 大 frame 在完整模式下先
get_metadata 定位再分区块拉;省调用模式下不这么做 —— 省上下文与省配额此消彼长,配额紧时优先省配额。
- 截图走 URL + curl 落盘,不要内联 base64(既吃上下文又不能给 pixdiff 用)。
references/layout-mapping.md 只在阶段 3 读。
可选人工旁路
遇到结构特别刁钻的 frame(深层嵌套 Auto Layout + 混合约束),可以让用户手动跑一下 FigmaToCode 插件,把它的确定性输出粘进来当「参考答案」,你负责改写成符合项目 token 与组件规范的版本。
这是旁路,不是必经路径 —— 该插件产出的是独立 scaffold,会绕过项目的 theme 层和现有组件,不能直接采用。
常见失败模式
| 现象 | 真实原因 | 对策 |
|---|
| 间距处处差几像素 | 用截图目测而非 get_design_context | 回阶段 1 拉真值 |
| 文字位置整体偏移 | 行高单位没换算 | 见映射表的行高小节 |
| 换个机型就崩 | 用绝对定位代替 Auto Layout | 回阶段 3 改布局语义 |
| 偏差数字大得离谱(>50%) | 截图范围不对(滚动位置 / 状态栏 / 裁剪) | 先修截图,再看数字 |
| 同一个颜色在多页面不一致 | 跳过了阶段 2 | 补 token 层 |
| 图标形状不对 | 手写 SVG 近似 | 用 download_assets 导出 |
| 基准图比实现图小一圈、细节糊 | maxDimension 用了默认 1024 | 按 original_width/height 重取 |
| MCP 报配额超限 | 每帧重复拉文件级数据 | 用 figma-cache.sh 缓存;或升级 seat |
| 换个屏幕宽度就崩 | RIGHT/STRETCH 约束被写成固定 left | 见映射表 §5.3 |