关于 penpot-design-md
penpot-design-md 可将 Penpot 设计文件整理为供人和编码 Agent 使用的便携 DESIGN.md。它记录实际读取的令牌、组件样式与视觉特征,不修改画布。
主要功能
- 01提取设计令牌和库资源
- 02采样组件样式与变体
- 03依据导出图像撰写视觉描述
- 04生成结构化的本地 DESIGN.md
- 05在 Known Gaps 中记录未知项
适用场景
- 01记录 Penpot 设计系统以便重建
- 02向人员和编码 Agent 传达设计细节
- 03在实现前找出缺失的设计信息
MCP 相关
它通过 Penpot MCP 工具读取文件结构和设计数据,并导出图像供视觉检查。技能不会修改画布,输出为本地 Markdown 文件。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ penpot/ penpot-ai-kit --skill penpot-design-md安装后在 Claude Code 中输入/penpot-design-md确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/01-extraction.md02-designmd-format.md03-inference.md04-designmd-template.mdscripts/extractDesignData.jssampleComponentStyles.js详细介绍
penpot-design-md — 从 Penpot 文件中提取 DESIGN.md 规范
1. 标题 + 工作原理
这项技能可将 Penpot 文件转换为 DESIGN.md:一份可移植的 Markdown 文档,让任何人类或
LLM 无需打开 Penpot,也能复现设计系统。文档包含承载设计令牌映射
(colors、typography、rounded、spacing、components)的 YAML frontmatter,以及说明系统行为的
文本章节(概览、各领域章节、注意事项、迭代指南、已知缺口)。
所有修改都通过 execute_code 进行;通过 export_shape 进行视觉验证;通过
penpotUtils.shapeStructure 读取结构(完整工具接口:shared/penpot-mcp-tool-reference.md)——但这项技能完全不修改画布:它只读取(设计令牌、资源库素材、抽样组件样式、导出的图片),并在 Agent 的
文件系统中写入一个本地 Markdown 文件。它在设计上采用多模态方式:美学描述(frontmatter 中的 description、概览、
注意事项)必须在查看实际导出内容之后编写,而不能仅凭设计令牌名称来写。
2. 最重要的一条规则
DESIGN.md 中的每个值都必须能追溯到你在文件中读取或看到的内容——绝不凭空捏造,也不使用“典型值”。 colors: 中出现的十六进制色值,必须来自设计令牌、资源库颜色或抽样形状填充。components: 中的内边距,必须来自真实组件的布局。“不要”项必须是在文件中观察到的(文件从未采用这种做法),而非臆想。如果无法提取某项内容,就将其列入已知缺口——坦诚说明缺口胜过给出看似合理的编造,因为下游 Agent 会根据这份文件构建内容。
3. Penpot MCP 工具参考
完整接口:shared/penpot-mcp-tool-reference.md。这项技能主要使用以下调用(均为只读):
| 需求 | 调用(除非另有说明,否则在 execute_code 内调用) |
|---|---|
| 页面清单 | penpotUtils.getPages()、penpotUtils.getPageByName(name) |
| 设计令牌集、主题、设计令牌 | penpot.library.local.tokens → sets[]、themes[]、set.tokens[](name、type、value、resolvedValue) |
| 资源库素材 | penpot.library.local → components[]、colors[]、typographies[] |
| 组件样式抽样 | component.mainInstance(),然后遍历子项:fills、strokes、borderRadius*、flex(内边距/间距)、文本 fontFamily/fontSize/fontWeight/lineHeight/letterSpacing、shape.tokens |
| 页面/画板结构 | penpotUtils.shapeStructure(shape, depth) |
| 视觉依据 | 在封面、关键组件画板和 1–2 个代表性界面上使用 export_shape(独立的 MCP 工具) |
依赖任何不熟悉的签名前,先通过 penpot_api_info 核实。
4. 插件 API 要点
这项技能容易踩中的问题(完整列表:shared/plugin-api-gotchas.md):
- #1 样式数组——读取
fills[0].fillColor等属性;不要尝试写入(这项技能不会修改内容)。 - 读取变体是安全的(#12 仅禁止修改)——可随意读取
variants.properties、variantComponents()[].variantProps,以记录变体轴。 - 资源库颜色重复——
library.local.colors可能包含多个短名称相同(default、hover)的条目,且属于不同分组;映射到语义名称前,要通过color.path`/分组消除歧义,或改用基于值的分组。 - 文本度量位于形状上——某些构建版本中的
fontSize是字符串;输出typography:时,将其规范化为 px 数值。 - 大型组件的
mainInstance()可能很慢——按批次抽样组件,每批约 5 个,每次调用一次execute_code;不要一次性全部处理。 - 使用前,通过
penpot_api_info核实不熟悉的签名。
5. 令牌感知型简报约定
提取前,先复述请求。请以资深设计系统文档撰写者的身份行事:读者是另一个 Agent,它将仅凭你的文件重建此系统。
- 上下文 — 涉及哪些 Penpot 文件和页面、产品是什么、谁会使用 DESIGN.md(人类、编码 Agent,还是两者)。
- 目标(单一) — 例如:“为 Pencil 设计系统编写
DESIGN.md,涵盖令牌、字体、形状语言,以及 Button/Input/Modal 组件系列。” - 输入 — 文件中的令牌集及主题、库组件/颜色/字体、要抽样的页面、文件的输出路径。
- 约束 — 范围(整个系统还是指定组件);哪个主题为规范主题(浅色/深色),以及如何记录另一主题;格式遵循 design-md 合约(参见
references/02-designmd-format.md)——不要自行另创章节名称。 - 验收标准(量化) —
- 每个
colors:条目都包含从文件中读取的十六进制值,并在正文或 Known Gaps 中注明其来源(令牌/库颜色/抽样填充); - 每个
typography:条目都包含从字体资源或抽样文本形状中读取的 fontFamily/fontSize/fontWeight/lineHeight/letterSpacing; components:涵盖已商定的组件列表,每个组件的值都通过{colors.*}/{typography.*}/{rounded.*}插值(存在已映射颜色时,不得使用原始十六进制值);- Overview 和 Do's & Don'ts 仅引用观察到的行为;撰写前至少实际查看过一项导出内容;
- Known Gaps 列出所有无法提取的领域(响应式行为、动效、层级等)。
- 每个
6. 强制工作流
视觉依据(强制): 此技能采用
shared/visual-self-review.md的变体——在导出并查看封面/关键屏幕之前,不得撰写description、Overview 或 Do's & Don'ts。仅凭令牌名称撰写的审美描述属于臆造。
阶段 0 — 发现(只读)
- 目标: 在确定提取计划前,盘点文件中提供的内容。
- 步骤:
- 每个会话调用一次
high_level_overview。 execute_code:运行scripts/extractDesignData.js→ 页面、令牌集(含主题及激活状态)、库组件/颜色/字体、计数。将结果缓存到storage.run.inventory。- 与用户商定范围(要深入记录哪些组件)、规范主题和输出路径。
- 每个会话调用一次
- 退出标准: 已完成清单盘点,并商定范围和输出路径。
- ✋ 检查点: 展示清单摘要和拟定的 DESIGN.md 范围(哪些章节会详细撰写,哪些内容会列入 Known Gaps)。
阶段 1 — 令牌与资源提取
- 目标: 为前置元数据映射表准备原始材料。
- 步骤: 按组执行
execute_code:导出颜色令牌(名称 → 解析后的值;令牌集不同时按主题分别导出)、尺寸/间距/圆角令牌、字体资源、库颜色(按组/路径去重)。根据令牌名称/层级推导语义映射(canvas、surface-*、ink-*、primary、hairline、semantic-*);若令牌中没有,则根据库颜色分组和使用情况抽样推导(参见references/03-inference.md)。 - 退出标准: 完成
colors:、typography:、rounded:、spacing:映射表草稿,并为每个条目注明来源。 - ✋ 检查点: 展示映射表草稿和所有未映射项。确认语义名称,避免其在文档中定型后难以更改。
第 2 阶段 — 组件采样
- 目标:
components:映射 + Components 正文部分。 - 步骤: 对每个范围内的组件(每次
execute_code最多处理 5 个):解析mainInstance(),读取背景/文本/边框/圆角/内边距/排版及绑定的令牌;捕获变体轴(variants.properties、variantProps)——各状态分别作为独立条目(button-primary-hover,等等)。通过第 1 阶段的映射插值变量值。 - 退出标准: 每个范围内的组件都有一个前置元数据条目和正文要点;未解析的值均已标记。
- ✋ 检查点: 在处理完整列表前,先展示 2–3 个组件条目示例,以供审核语气和格式。
第 3 阶段 — 视觉特征审查
- 目标: 如实描述美学特征。
- 步骤: 对封面和 1–3 个有代表性的画板/屏幕执行
export_shape。查看它们。 撰写:前置元数据中的description(一段内容紧凑的文字,涵盖主要表面、强调色的使用、字体特征、形状语言和节奏)、Overview(概述)及 Key Characteristics(关键特征)要点,以及 Do's & Don'ts(注意事项与禁忌;仅包含文件本身展现的规则或其令牌结构所暗示的规则)。 - 退出标准: 所写内容以实际观察为依据;每条禁忌都能追溯到一项观察(“没有任何胶囊按钮”“只有一种强调色调”)。
- ✋ 检查点: 将导出结果与拟好的文字并列展示。
第 4 阶段 — 组装、自查并写入文件
- 目标: 交付成果。
- 步骤: 按照
references/02-designmd-format.md组装(章节顺序固定);运行格式自检(前置元数据可解析为 YAML、每个{ref}都能对应到一个前置元数据键、存在colors:条目时components:中不得出现原始十六进制值、Known Gaps 不得为空,除非内容确实完整);将文件写入双方约定的路径。 - 退出标准: 文件已写入;自检通过。
- ✋ 检查点: 展示文件(或其大纲和关键摘录),以供最终确认。
7. 关键规则
- 画布只读。 绝不修改形状、令牌或库。唯一的写入操作是本地 Markdown 文件。
- 不得编造值。 所有十六进制值/像素值/字重都必须从文件中读取。未知值 → Known Gaps。
- 引用插值,不重复罗列。 正文文字和
components:应引用{colors.x}/{typography.y}/{rounded.z};原始值仅在前置元数据映射中出现一次。 - 先查看,再描述。 至少查看一个
export_shape的结果后,才能撰写美学描述(第 3 阶段)。 - 明确主题。 如果文件包含浅色/深色两套设置,应在映射中记录规范主题,并将另一套作为差异小节记录——绝不要悄悄混用两套值。
- 按组对库中的颜色去重后,再进行语义映射;不同组中相同的短名称代表不同的颜色。
- 整理后披露。 对于包含 100 多个令牌的文件,应归纳为语义分组,并说明合并了哪些内容(“107 个全局令牌 → 14 个语义颜色;完整集合名称见 Known Gaps”)——不得悄悄截断。
- 每次
execute_code调用只执行一个提取步骤;组件采样每批最多处理 5 个。 - 变体轴是重要的文档内容——对包含变体轴的组件,记录
Property=Value矩阵;读取这些信息是安全的。 - 输出必须通过格式检查——章节顺序和前置元数据键必须与
references/02-designmd-format.md完全一致。
8. 领域架构
DESIGN.md 规范(摘要——完整规范见 references/02-designmd-format.md):
--- (YAML frontmatter)
version, name, description # description = one dense aesthetic paragraph
colors: { semantic-name: "#hex", ... } # canvas/surface-*/ink-*/primary/hairline/semantic-*
typography: { style-name: {fontFamily,fontSize,fontWeight,lineHeight,letterSpacing}, ... }
rounded: { xs..pill: Npx }
spacing: { xxs..section: Npx }
components: { component-or-state: {backgroundColor,textColor,typography,rounded,padding,...}, ... }
---
## Overview # prose + **Key Characteristics** bullets
## Colors # Brand & Accent / Surface / Text / Semantic subsections, sources cited
## Typography # Font Family / Hierarchy / Principles / substitutes note
## Layout # Spacing System / Grid & Container / Whitespace Philosophy
## Elevation & Depth # shadows/borders ladder (or "flat by design")
## Shapes # Border Radius Scale + shape language
## Components # per component: **`name`** + purpose + property bullets with {refs}
## Do's and Don'ts # observed rules only
## Responsive Behavior # only if derivable from the file; else Known Gaps
## Iteration Guide # how a future agent should edit designs with this doc
## Known Gaps # everything not extractable — mandatory honesty section
运行记录键(见 §10):inventory、maps、componentEntries、exportsViewed、gaps。
9. 模式与策略
参见 shared/modes-and-policies.md。此技能为 suggest 模式,并始终保持该模式:它不会修改画布,因此不存在安全应用集合,也没有任何内容可自动应用。唯一的副作用——写入 Markdown 文件——会在第 4 阶段的检查点执行,路径使用用户在第 0 阶段批准的路径。
覆盖现有 DESIGN.md 需要明确确认(请先展示差异摘要)。
10. 状态管理
参见 shared/state-management.md。RUN_ID slug 示例:designmd-2026-07-09-a。会话缓存位于
storage.run.*(清单、映射、组件条目、已查看的导出、缺口)。此技能为只读,因此
不会写入 setSharedPluginData,除非用户要求持久化指向生成文档的指针(designmd.lastGenerated = 路径 + 日期)。恢复时:重新读取 storage.run;如果已不存在,
则重新运行第 0 阶段(提取开销低且幂等)。
11. 用户检查点
| 阶段后 | 展示的产物 | 询问内容 |
|---|---|---|
| 0 发现 | 清单(页面、集合、主题、资源)+ 拟定范围和输出路径 | “请确认范围、规范主题以及文件写入位置。” |
| 1 设计令牌 | 草拟的 frontmatter 映射 + 未映射值 | “是否批准这些语义名称/指出映射错误?” |
| 2 组件 | 2–3 个组件条目示例 | “语气和详略程度是否合适?确认后我会补全列表。” |
| 3 视觉 | 导出内容 + 草拟的描述/概述/注意事项 | “这些文字是否符合你看到的内容?” |
| 4 汇总 | 已编写的文件(大纲 + 摘录) | “请最终确认——已知缺口中还有什么要补充的吗?” |
12. 命名约定
参见 shared/naming-conventions.md。Frontmatter 键使用 kebab-case 语义名称,遵循
design-md 惯例(ink-muted、surface-2、display-xl、button-primary-hover)——将
Penpot 的点号表示法令牌映射到这些名称,并在 Colors 部分记录映射关系(color.text.muted → ink-muted),
以便文档可以追溯到源文件。组件条目使用组件的 kebab-case 名称,并根据变体值派生
-hover/-pressed/-disabled/-selected 状态后缀。
13. 反合理化对照表
| LLM 给出的借口 | 错误原因 | 确定性应对措施 |
|---|---|---|
| “文件里没有令牌,所以我会写一套常见配色。” | 编造的值会污染文档的所有下游使用者。 | 停止。采样真实的形状填充(references/03-inference.md),映射现有内容,其余内容列入已知缺口。 |
| “我可以从令牌名称描述美学风格。” | color.primary = #00d1b8 并不能说明设计是紧凑还是疏朗、扁平还是有层次。 | 在至少查看 1 个 export_shape 前,暂停第 3 阶段。文字描述必须引用观察到的内容。 |
| “107 个令牌太多了;我就原样列出来吧。” | 原始转储不是文档——此格式要求按语义组进行整理。 | 整理到语义映射中,然后在 Colors + Known Gaps 中明确说明归并情况。 |
| “这个组件一目了然——标准按钮,8px 圆角。” | “标准”是编造的;此文件中的按钮可能是 6px 圆角并带有极细边框。 | 编写任何组件条目前,先采样实际的 mainInstance()。 |
| “注意事项本来就是通用最佳实践。” | 此部分记录的是这个系统的规则,而不是设计常识。 | 每条注意事项都必须能追溯到一项观察(令牌结构、采样样式或已查看的导出内容)。 |
| “浅色和深色值不同;我会取平均值或按属性挑选。” | 混合主题值描述的是一个并不存在的设计。 | 选择规范主题(第 0 阶段的决策);将另一个主题记录为差异小节。 |
14. 辅助代码片段
清单(第 0 阶段——或使用 scripts/extractDesignData.js):
const lib = penpot.library.local;
return {
pages: penpotUtils.getPages().map(p => p.name),
sets: (lib.tokens.sets || []).map(s => ({ name: s.name, active: s.active, count: (s.tokens||[]).length })),
themes: (lib.tokens.themes || []).map(t => ({ group: t.group, name: t.name })),
components: (lib.components || []).length, colors: (lib.colors || []).length,
typographies: (lib.typographies || []).map(t => t.name)
};
转储带解析结果的颜色令牌(第 1 阶段):
const out = [];
for (const s of penpot.library.local.tokens.sets || []) {
if (!s.active) continue; // canonical theme only — dump inactive sets separately as the delta
for (const t of s.tokens || []) if (t.type === "color")
out.push({ set: s.name, name: t.name, value: t.value, resolved: String(t.resolvedValue) });
}
return { count: out.length, tokens: out };
采样单个组件的样式(第 2 阶段):
const comp = penpot.library.local.components.find(c => c.name === "REPLACE-ME");
const root = comp.mainInstance();
const texts = penpotUtils.findShapes(s => s.type === "text", root).slice(0, 3);
return {
name: comp.name,
bg: root.fills && root.fills[0] ? root.fills[0].fillColor : null,
radius: root.borderRadius ?? null,
padding: root.flex ? [root.flex.topPadding, root.flex.rightPadding, root.flex.bottomPadding, root.flex.leftPadding] : null,
gap: root.flex ? [root.flex.rowGap, root.flex.columnGap] : null,
boundTokens: root.tokens || {},
text: texts.map(t => ({ family: t.fontFamily, size: t.fontSize, weight: t.fontWeight, lh: t.lineHeight, ls: t.letterSpacing, fill: t.fills && t.fills[0] && t.fills[0].fillColor })),
variantAxes: comp.isVariant && comp.isVariant() ? comp.variantProps : null
};
15. 参考资源
penpot_api_info("TokenCatalog")、("LibraryComponent", "mainInstance")、("Variants")、("LibraryTypography")——在第 1/2 阶段前进行验证。shared/penpot-mcp-tool-reference.md(工具接口)、shared/plugin-api-gotchas.md(易踩的坑)、shared/tokens-schema.json(层级、Penpot 令牌类型字符串)。- 此格式的规范骨架:
references/04-designmd-template.md(自包含——无需外部语料)。
16. 支持文件
参考资料(渐进式披露——在相应阶段运行时加载)
| 文件 | 阅读阶段 | 内容 |
|---|---|---|
references/01-extraction.md | 阶段 0–2 | 在哪里读取什么:令牌集/主题、库资源、组件采样方法、变体维度、主题处理 |
references/02-designmd-format.md | 阶段 3–4 | 完整的 DESIGN.md 规范:frontmatter 架构、章节顺序、插值规则、语气、自检清单 |
references/03-inference.md | 阶段 1(令牌较少的文件) | 如何在没有令牌的情况下推导语义映射:库颜色分组、填充频率采样、表面层级检测 |
references/04-designmd-template.md | 阶段 4 | 可填写的 DESIGN.md 框架(通用且自包含)——复制后填入提取的值,并删除指导说明 |
脚本(粘贴到 execute_code 调用中;替换占位符)
| 脚本 | 用途 |
|---|---|
scripts/extractDesignData.js | 阶段 0——一次调用即可进行只读清点:页面、集合、主题、资源、计数 |
scripts/sampleComponentStyles.js | 阶段 2——批量采样最多 5 个组件的解析后样式、绑定令牌和变体维度 |
规范路径。 在原生安装中,shared/… 和 policies/… 会在此捆绑包内解析(由安装程序随附);在 Claude Code 插件安装中,它们位于插件根目录——${CLAUDE_PLUGIN_ROOT}/shared/…,也就是本文件向上两级的目录。