mcpField
P
penpot-design-to-code-review

penpot-design-to-code-review

github.com/penpot测试与质量108125 次安装3 周前更新
代码仓库

关于 penpot-design-to-code-review

面向设计工程师的只读 Penpot 技能,可将选中的设计与提供的组件代码对照,并生成结构化偏差报告。没有代码时,可改为检查设计与现有系统令牌的一致性。

主要功能

  1. 01对照 Penpot 选区与组件代码
  2. 02检查结构、样式和绑定令牌
  3. 03导出设计图像用于比对
  4. 04分类并报告设计与代码偏差
  5. 05保持画布不变

适用场景

  1. 01核对 React 或 Vue 组件与 Penpot 选区
  2. 02检查令牌、间距、字体和状态差异
  3. 03无代码来源时审查设计令牌

MCP 相关

使用 Penpot MCP 工具检查选区和令牌、提取标记与样式,并导出形状用于视觉比对。不会修改画布。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/penpot/penpot-ai-kit --skill penpot-design-to-code-review

安装后在 Claude Code 中输入/penpot-design-to-code-review确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
01-extract-markup-style.md
02-mapping-to-source.md
03-storybook-compare.md
04-drift-report.md
scripts/
exportForCompare.js
extractMarkupStyle.js
generateDriftReport.js

详细介绍

penpot-design-to-code-review — 弥合设计↔代码之间的 READ 方向鸿沟

1. 标题 + 工作原理

此技能读取 Penpot 中的选区,并将其与工程师实现的代码进行比较,然后报告偏差。所有变更都通过 execute_code 执行;使用 export_shape 进行视觉验证;使用 penpotUtils.shapeStructure 读取结构(完整工具接口:shared/penpot-mcp-tool-reference.md)。它使用 penpotUtils.tokenOverview 读取令牌,通过 penpot.generateMarkup(shapes, opts) 和 penpot.generateStyle(shapes, opts) 提取设计的 HTML/CSS(两者都通过 execute_code 调用),并使用单独的 export_shape 工具生成视觉图像以供并排比较。此技能默认对画布为只读(mode: suggest)——绝不修改设计;它只生成报告,并且仅在明确请求时提出协调步骤。代码侧(React/Vue 等组件文件,或已渲染的 Storybook story/其 DOM+CSS)由用户或其他 MCP(文件系统、Storybook、浏览器工具)提供。如果没有可用的代码来源,此技能会优雅降级,改为将选区与设计系统令牌进行比较。


2. 最重要的一条规则

绝不要一次性完成审查,也绝不要在审查期间进行修改。 提取 → 规范化 → 映射 → 比较差异 → 报告;每一步使用一次独立的逻辑 execute_code 调用,并在各阶段之间进行读取或调用 export_shape。一次性拼凑出的偏差报告无法验证,而且几乎总会误判令牌名称。这是一个 suggest 模式的技能:不会触碰画布。你只需生成一份结构化的 DRIFT 报告;如需协调修改,则交由可写入的技能(令牌相关工作交给 penpot-foundations,结构重建交给 penpot-rename-layers 或 penpot-build-from-code)处理,并遵循各自的检查点。


3. Penpot MCP 工具参考

完整接口:参见 shared/penpot-mcp-tool-reference.md。此技能主要使用以下调用:

需求调用(除非另有说明,否则在 execute_code 内)
解析选区penpot.selection(形状数组)
读取选区的对象树penpotUtils.shapeStructure(shape, maxDepth)
读取活动令牌(名称 → 解析后的值)penpotUtils.tokenOverview()
根据原始值查找令牌penpotUtils.findTokensByName(name) / findTokenByName(name)
生成设计的 HTMLpenpot.generateMarkup(shapes, opts)
生成设计的 CSSpenpot.generateStyle(shapes, opts)
检查形状绑定的令牌shape.tokens(属性 → 令牌的映射)
检查原始样式值shape.fills、shape.strokes、shape.shadows、shape.borderRadius,以及通过 shape.width/height/bounds 获取的几何信息
生成比较图像export_shape(独立的 MCP 工具,不是 JS)——每个状态画板调用一次

依赖任何不熟悉的签名之前,请使用 penpot_api_info 验证(例如,penpot_api_info("Page", "generateMarkup")、penpot_api_info("Shape", "tokens"))。


4. 插件 API 要点

注意事项编号对应 shared/plugin-api-gotchas.md。以下是本技能需要特别注意的事项:

  • generateMarkup / generateStyle 接受的是形状数组,而不是单个形状,并且接受一个 opts 对象。传入选项前,请通过 penpot_api_info("Penpot", "generateMarkup") / ("Penpot", "generateStyle") 确认准确的 opts 键名——猜错选项名称只会静默地产生默认输出,让你误以为「没有偏差」。直接传入 penpot.selection(它已经是数组)。
  • #1 样式数组中的各项不可变——可以读取(shape.fills[0].fillColor);绝不要尝试在此处「修复」填充——本技能不会进行修改。
  • #5 width/height/parentX/parentY 为只读属性——读取几何信息以进行差异比较;此处不要调用 resize()/setParentXY()。
  • #2 应用令牌是异步的(约 100 ms)——读取时无关紧要,但如果协调交接流程应用了令牌,不要在同一次调用中读回它(避免错误地判定失败)。
  • shape.tokens 是哪个设计令牌绑定到哪个属性的权威映射。优先使用它,而不是根据原始十六进制值猜测令牌。没有绑定令牌的原始值本身就是一项偏差发现(「设计使用了硬编码值,而非令牌」)。
  • generateStyle 输出的是设计所理解的 CSS——它不知道你代码中的类名或变量。将其输出视为设计的意图,然后在比较差异前对双方进行规范化(见 references/04)。不要直接比较原始 CSS 字符串。
  • 验证,不要猜测。 当 generateMarkup/generateStyle 返回意外结果时,首先应怀疑 opts 键名错误或参数不是数组——用 penpot_api_info 重新确认,不要换个猜法再试。

5. 令牌感知型简报约定

开始提取任何内容前,先将请求重述为一份约定。以资深设计工程师的身份行事,将设计文件视为权威规范,绝不接受存在令牌时仍使用硬编码值。

  • 上下文——审查对象是什么(单个组件、一组状态、屏幕区域),以及面向谁(哪个应用/库,代码使用哪种框架)。
  • 目标——单一且具体:*「确认 src/Button.tsx 中的 <Button variant=primary size=md> 与 Penpot 选区一致,包括悬停/禁用状态。」*不要只说「审查设计」。
  • 输入 —
    • Penpot 选区(解析它;若为空,请用户选择对象或指定画板);
    • 代码源:文件路径/粘贴的组件代码,或 Storybook 故事(URL 或渲染后的 DOM+CSS),或明确指定无;
    • 当前使用的令牌集(tokenOverview()),以及语义令牌应解析到哪个层级。
  • 约束——明确各维度由哪一方作为权威来源(通常:视觉令牌、间距、状态以设计为准;行为/无障碍语义以代码为准);仅使用 4px 网格上的令牌值;本技能禁止修改画布。
  • 验收标准(量化) —
    • 每项设计属性都归类为 match / drift / code-only / design-only;
    • 每项颜色/间距/圆角/字体偏差都注明涉及的令牌(或标注「未绑定令牌」);
    • 颜色偏差报告十六进制级差异,并在适用时报告 WCAG 对比度影响;
    • 间距偏差报告 px 差值,以及该值是否符合 4px 网格;
    • 状态覆盖情况以矩阵形式报告(设计状态 × 代码状态),并明确列出缺口;
    • 根据 §8 的分级标准为每项发现标注严重程度(Critical / Major / Minor)。

6. 强制工作流

阶段 0 — 发现(只读)
  • 目标: 在查看任何样式之前,了解正在审查的内容以及现有的令牌。
  • 步骤:
    1. high_level_overview(每个会话调用一次)。
    2. execute_code:解析 penpot.selection;如果为空,则停止并请用户选择组件/画板。返回 id、名称、类型和数量。
    3. execute_code:对每个顶层选中图形调用 penpotUtils.shapeStructure(sel[0], 4) — 捕获结构树(名称、类型、嵌套关系)。
    4. execute_code:调用 penpotUtils.tokenOverview() — 捕获令牌集、名称和解析后的值。
    5. 与用户确认代码来源(文件路径 / 粘贴的代码 / Storybook / 无)。
  • 退出标准: 能够说出设计的结构、令牌以及代码来源(或确认没有代码来源)。
  • ✋ 检查点: 展示结构摘要 + 令牌清单 + 用于对比的代码来源。请用户确认范围,以及各个维度以哪一方为准。
阶段 1 — 从设计中提取标记和样式
  • 目标: 将所选内容转换为规范化的 HTML + CSS + 令牌映射。
  • 步骤: 使用 scripts/extractMarkupStyle.js — 调用一次 execute_code,运行 penpot.generateMarkup(sel, opts)、penpot.generateStyle(sel, opts),遍历每个节点的 shape.tokens,并返回 { markup, style, perNode:[{id,name,type,tokens,rawFills,rawSpacing,radius,typography}] }。
  • 退出标准: 已获取设计的 HTML、CSS 和逐节点令牌/原始值映射。
  • ✋ 检查点: 展示提取出的标记 + 样式(精简版)和令牌映射。确认后再进行映射。
阶段 2 — 获取对比图像
  • 目标: 提供一张图像,供工程师与代码渲染结果进行目视对比。
  • 步骤: 运行 scripts/exportForCompare.js,收集图形 id、边界和元数据;然后调用 export_shape MCP 工具,使用 'selection'(或已捕获的 id),并设置 format: png。对于多状态组件,分别导出每个状态画板。请工程师提供代码/故事的渲染截图(如有可用的浏览器 MCP,也可以通过它获取)。
  • 退出标准: 已获取设计图像,并与对应的代码渲染结果配对。
  • ✋ 检查点: 并排展示;让工程师指出明显的视觉差异,以便确定优先级。
阶段 3 — 映射设计 ↔ 源代码
  • 目标: 确定设计节点/令牌/状态与代码元素/变量/属性之间的对应关系。参见 references/02。
  • 步骤: 解析提供的代码(属性、CSS/变量用法、状态类/变体);建立映射表 designNode → codeElement、designToken → codeVar、designState → codeState。如果代码来源是 Storybook 故事,则根据故事的 args/controls 推导状态(参见 references/03)。如果没有代码来源: 仅将设计令牌映射到设计系统令牌集,并跳过代码列(平稳降级)。
  • 退出标准: 映射表中的每个设计属性都关联了对应的代码项,或标记为未映射。
  • ✋ 检查点: 展示映射表;在进行差异比对前修正错误映射。
阶段 4 — 比对并生成 DRIFT 报告
  • 目标: 交付成果。
  • 步骤: 运行 references/04 中的比对逻辑,使用 scripts/generateDriftReport.js 汇总(结构化输出),为每项发现分类(match/drift/code-only/design-only),指定严重程度,并为每项不匹配提出协调建议。对于任何影响对比度的颜色差异,注明 WCAG 编号。
  • 退出标准: 报告完整涵盖令牌、结构、间距、排版和状态,并包含严重程度和协调建议。
  • ✋ 检查点: 展示报告。如果用户希望应用修复,则交接给适当的写入技能 — 不要在此处进行修改。

7. 关键规则

  1. 对画布只读。 此技能绝不会调用任何会产生变更的方法。对齐工作会移交给下游技能,并受其自身检查点约束。
  2. 绝不直接比较原始 CSS 字符串。 比较差异前,先规范化两侧(单位转换为 px、颜色转换为小写十六进制、展开简写属性、对属性排序)。参见 references/04。
  3. 没有绑定令牌的硬编码值属于问题,即使其视觉效果与代码一致。将其报告为 Minor 级设计规范问题(“值未使用令牌”)。
  4. 每条视觉问题都要注明令牌名称。 先读取 shape.tokens;只有未绑定令牌时,才通过 tokenOverview() 匹配原始十六进制颜色。
  5. 间距差异需注明 px 数值和网格对齐情况。 任何非 4 的倍数的值都会被标记,无论它是否与代码一致。
  6. 信息不足时降级处理,不要猜测。 没有代码来源时,将设计系统令牌作为比较依据,并在报告标题中说明;绝不捏造代码侧信息。
  7. 每个逻辑步骤只调用一次 execute_code,并按顺序执行,在各阶段之间设置检查点。绝不批量执行提取、映射和差异比较。
  8. 传入选项前,使用 penpot_api_info 确认 generateMarkup/generateStyle 的 opts。
  9. 进行任何提取前,确认选择非空; 空选择会静默地产生空标记。
  10. 状态覆盖情况应以矩阵呈现,而非勾选框。 报告每个(设计状态 × 代码状态)单元格。

8. 领域架构

DRIFT 报告架构(即 generateDriftReport.js 返回并由你渲染的内容):

{
  runId, scope, codeSource: "file" | "storybook" | "none",
  authoritative: { visual: "design", behavior: "code", ... },
  summary: { match, drift, codeOnly, designOnly, critical, major, minor },
  findings: [
    {
      id,                         // stable, e.g. "drift-color-01"
      dimension: "color" | "spacing" | "typography" | "radius" | "structure" | "state",
      designNode, codeElement,    // either may be null
      designValue, codeValue,     // normalized
      token,                      // bound design token name or null
      status: "match" | "drift" | "design-only" | "code-only",
      delta,                      // px delta, hex delta, contrast delta, etc.
      grid4px,                    // boolean | null (spacing only)
      wcag,                       // criterion string if contrast-impacting, else null
      severity: "Critical" | "Major" | "Minor",
      reconciliation              // concrete, one-line suggested fix + which side to change
    }
  ],
  stateMatrix: { design: [...], code: [...], cells: [{ state, design: bool, code: bool }] }
}

此结构在 shared/report-schemas/drift-report.schema.json 中有正式定义;需包含派生的顶层 drift 计数(= summary.drift + summary.designOnly + summary.codeOnly),code-to-penpot-sync 工作流会根据此值分支处理。

严重级别标准:

严重级别定义
Critical影响功能或无障碍体验的对等性破坏:交互元素在设计中存在某个状态,但代码中不存在(反之亦然);颜色差异导致对比度低于 WCAG 1.4.3 AA(4.5:1 / 3:1)。
Major用户会注意到的明显视觉不匹配:绑定了错误的语义令牌、间距偏差 ≥ 8px、字号/字重不匹配、缺少结构元素(图标、标签)。
Minor规范性或难以察觉的问题:值与设计匹配但未使用令牌、间距差异 ≤ 4px、顺序/命名差异、可选的装饰性偏差。

比较的维度: 令牌使用情况(使用哪个令牌、已绑定还是硬编码)、结构(节点树与 DOM 树)、间距(4px 网格上的内边距/间隙/外边距)、排版(字体系列/字号/字重/行高/字间距)、圆角/边框,以及状态覆盖情况。


9. 模式与策略

参见 shared/modes-and-policies.md。此技能默认采用 suggest 模式,并且始终保持该模式——它是一项审计。它不会自动对画布进行任何应用操作。此技能没有安全应用范围,因为它不会进行任何变更;其“输出”就是报告。如果用户要求修复差异,此技能会明确移交处理:令牌替换 → penpot-foundations(apply-with-review)、图层命名 → penpot-rename-layers、结构/几何变更 → penpot-build-from-code。每项对齐操作都会作为提案提交,并注明事实来源一侧;未经下游技能的检查点确认,不会进行任何变更。


10. 状态管理

参见 shared/state-management.md。RUN_ID slug 示例:d2c-2026-06-05-a。通过 storage 写入的账本键(此技能对画布为只读,因此除非用户选择持久化报告,否则不会写入 setSharedPluginData):

  • storage.run.scope — 已解析的选区 ID/名称。
  • storage.run.tokens — 缓存的 tokenOverview() 结果。
  • storage.run.extracted — Phase 1 中的 { markup, style, perNode }。
  • storage.run.mapping — Phase 3 中的设计↔代码映射表。
  • storage.run.report — 最终的 DRIFT 报告对象。

**恢复:**重新读取 storage.run;如果不存在(新会话),则重新运行 Phase 0 发现,并基于 shapeStructure + tokenOverview 重新推导,而不是信任过期报告。只有在用户希望将报告持久化到文件中以便交接时,才可选择将报告同步到 setSharedPluginData(NS, "${RUN_ID}.driftReport", JSON.stringify(report))。


11. 用户检查点

阶段之后展示的产物要询问的内容
0 发现结构摘要、令牌清单、已识别的代码来源“确认范围,以及哪些维度以哪一侧为准。”
1 提取精简后的标记 + 样式、逐节点令牌映射“这是否符合你预期设计中声明的内容?”
2 视觉export_shape PNG(s) 与代码渲染结果配对展示“标出明显的视觉差异,以便确定优先级。”
3 映射设计↔代码映射表“更正映射错误的节点/令牌/状态。”
4 报告完整的 DRIFT 报告(摘要 + 发现 + 状态矩阵)“请批准。是否要将协调工作交接给写入技能?”

12. 命名约定

参见 shared/naming-conventions.md。此技能会读取名称;它依赖语义化名称(button、label、icon、h1–h6)将设计节点映射到 DOM 元素。自动生成的名称(Rectangle 12、Board)会降低映射质量——如果选区中这类名称很多,建议先运行 penpot-rename-layers,并在报告中注明结构差异判断的置信度较低。令牌会根据 shared/tokens-schema.json 中的点号表示法分类体系进行匹配;令牌的 type 字符串使用 Penpot 的精确拼写(color、spacing、borderRadius、fontSizes 等)。代码变量(CSS 自定义属性、JS 主题键)会在映射前规范化为该点号表示法(例如 --color-action-primary-bg ↔ color.action.primary.bg)。


13. 反合理化对照表

LLM 给出的借口为什么不对确定性应对措施
“CSS 字符串不一样,所以出现了偏差。”generateStyle 输出 16px,代码输出 1rem;#FFF 与 #ffffff;简写与完整写法。直接比较原始字符串会产生无用结果。先规范化两边(单位→px,颜色→小写 6 位十六进制,展开简写,按属性排序)。只比较规范化后的映射。
“十六进制值与代码一致,所以此属性通过。”十六进制值匹配,但未绑定令牌仍会破坏主题化和治理——它在深色模式下不会切换,而且不会发出提示。读取 shape.tokens。如果未绑定令牌,即使十六进制值匹配,也要报告 Minor 级别的“值未使用令牌化”问题。
“没有提供 Storybook/代码,所以我无法审查。”此技能必须能够优雅降级。切换到设计系统令牌模式:将所选内容与 tokenOverview() 进行比较,在报告头部设置 codeSource: "none",并仅报告设计侧问题。
“generateStyle 返回的内容几乎为空——设计几乎没有样式。”更可能是你传入了单个形状,而不是数组,或使用了错误的 opts 键。重新检查:传入 penpot.selection(一个数组);使用 penpot_api_info 验证 opts。重新提取后再得出“没有偏差”的结论。
“我顺手在 Penpot 里修复令牌不匹配吧。”这是一个 suggest 模式的只读技能;进行修改会违反约定,并跳过后续检查点。不要修改。将协调方案作为提议输出,并移交给 penpot-foundations/penpot-build-from-code。
“两边间距都是 18px,所以匹配——通过。”18 不符合 4px 网格;与代码匹配并不代表数值正确。无论是否与代码一致,都要将所有不是 4 的倍数的间距标记为问题(grid4px:false);建议使用最接近的网格令牌。

14. 辅助代码片段

解析并汇总所选内容(阶段 0):

const sel = penpot.selection || [];
return sel.length === 0
  ? { empty: true, hint: "Select the component/board to review, then re-run." }
  : { count: sel.length, items: sel.map(s => ({ id: s.id, name: s.name, type: s.type })) };

读取节点上绑定的令牌与原始值(阶段 1):

const s = penpotUtils.findShapeById("REPLACE-ME-shape-id");
const tokens = s.tokens ? Object.fromEntries(Object.entries(s.tokens)) : {};
const fill = Array.isArray(s.fills) && s.fills[0] ? s.fills[0].fillColor : null;
return { id: s.id, name: s.name, boundTokens: tokens, rawFillColor: fill, radius: s.borderRadius ?? null };

未绑定令牌时,将原始十六进制值与现有令牌匹配(阶段 4 备用方案):

const overview = penpotUtils.tokenOverview();
const target = "REPLACE-ME-#0066ff".toLowerCase();
// scan resolved color tokens for an exact value match
const hit = (overview.tokens || []).find(t =>
  t.type === "color" && String(t.resolvedValue).toLowerCase() === target);
return { rawValue: target, suggestedToken: hit ? hit.name : null };

15. 参考资源

  • penpot_api_info("Penpot", "generateMarkup") 和 ("Penpot", "generateStyle") — 在阶段 1 前确认 opts 键。
  • penpot_api_info("Shape", "tokens") — 确认绑定令牌映射的结构。
  • penpot_api_info("Page", "shapeStructure") / penpotUtils — 读取结构。
  • shared/penpot-mcp-tool-reference.md — 工具接口和全局变量。
  • shared/plugin-api-gotchas.md — 不可变数组、异步令牌、只读几何信息。
  • shared/tokens-schema.json — 令牌类型字符串、层级、4px 网格规则。
  • W3C WCAG 1.4.3(对比度),用于提升颜色偏差的严重程度。

16. 支持文件

参考资料(渐进式披露——在相应阶段运行时加载)
文件阅读时机内容
references/01-extract-markup-style.md阶段 1generateMarkup/generateStyle 用法、opts、读取 shape.tokens 和原始值、构建逐节点映射
references/02-mapping-to-source.md阶段 3解析提供的组件代码、将设计节点映射到 DOM、将令牌映射到 CSS 变量/主题键、将状态映射到 props/类
references/03-storybook-compare.md阶段 3(Storybook 场景)从 story 参数/控件推导状态,捕获渲染后的 DOM 和 CSS,仅提供 URL 时的优雅降级
references/04-drift-report.md阶段 4规范化规则、差异算法、严重程度标准、报告呈现、协调措辞
脚本(粘贴到 execute_code 调用中;替换占位符)
脚本用途
scripts/extractMarkupStyle.js阶段 1 — 为所选内容生成 generateMarkup + generateStyle + 每个节点的令牌/原始值映射
scripts/exportForCompare.js阶段 2 — 收集形状 ID、边界和元数据,以便调用方调用 export_shape MCP 工具
scripts/generateDriftReport.js阶段 4 — 根据提取的设计数据和映射后的代码数据,组装结构化 DRIFT 报告

规则文件路径。 在原生安装中,shared/… 和 policies/… 在此软件包内解析(由安装程序随附);在 Claude Code 插件安装中,它们位于插件根目录 — ${CLAUDE_PLUGIN_ROOT}/shared/…,相对于此文件向上两级。

评论

支持 Markdown