关于 penpot-foundations
用于在 Penpot 中建立和治理设计令牌基础的技能,也能从现有设计推导令牌。它将令牌分为基础、语义及可选组件层级,并通过检查点验证引用、绑定与间距。
主要功能
- 01创建基础与语义令牌层级
- 02从现有设计推导令牌
- 03将图形绑定到语义令牌
- 04设置浅色与深色模式
- 05验证引用关系与间距
适用场景
- 01为设计系统建立令牌
- 02从硬编码设计提取令牌
- 03绑定现有图形以支持主题切换
- 04检查令牌引用与间距规则
MCP 相关
该技能通过 `execute_code` 使用 Penpot MCP 工具,包括 `penpotUtils.tokenOverview`、`shapeStructure` 以及令牌创建和应用接口。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ penpot/ penpot-ai-kit --skill penpot-foundations安装后在 Claude Code 中输入/penpot-foundations确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/01-discovery.md02-token-architecture.md03-infer-from-design.md04-themes-modes.md05-naming.md06-anti-rationalization.md07-error-recovery.mdscripts/applyTokensToShapes.jsbindTokensToElements.jscreateSemanticTokens.jscreateThemes.jscreateTokenSet.jsinferTokensFromShapes.jsinspectFileStructure.jsvalidateTokens.js详细介绍
penpot-foundations — 令牌与组件库基础
1. 标题 + 工作原理
penpot-foundations 是工具包中承重的技能:没有受治理的令牌层,下游的任何内容都无法正确工作。所有变更都通过 execute_code 执行;使用 export_shape 进行视觉验证;使用 penpotUtils.shapeStructure 读取结构(完整工具接口:
shared/penpot-mcp-tool-reference.md)。此技能使用 penpotUtils.tokenOverview() 读取令牌,通过 penpot.library.local.tokens(addSet、set.addToken({type,name,value}))创建令牌,并使用 shape.applyToken(token, properties) 将其绑定到形状。它分为三个层级——原始 → 语义 → 组件(参见 shared/tokens-schema.json)——也可以从现有的硬编码设计中推断令牌系统。
2. 最重要的一条规则
一切从令牌开始,绝不一次性完成。 分小步构建基础,确保操作幂等:先创建原始令牌,再创建引用它们的语义别名,最后(仅在需要时)创建组件令牌。每个层级完成后都使用 tokenOverview() 验证。绝不默默编造值——先提出比例,再在检查点让人工审核批准。
3. Penpot MCP 工具参考
完整接口:shared/penpot-mcp-tool-reference.md。此技能依赖的领域调用:
penpotUtils.tokenOverview() / shapeStructure()(发现 + 推断);
penpot.library.local.tokens.addSet / set.addToken / shape.applyToken(写入);
在依赖成员签名之前,调用 penpot_api_info('TokenSet'|'Token'|'TokenCatalog')。
4. Plugin API 要点
注意事项编号参见 shared/plugin-api-gotchas.md。
- #8 令牌创建——
penpot.library.local.tokens;addSet({name})/addTheme({group,name})接受 OBJECT 参数;新集合处于非活动状态(set.toggleActive());令牌的value始终是字符串(字面值或"{reference}");读取token.resolvedValue。 - 使用
shared/tokens-schema.json中完全一致的令牌type字符串:color、dimension、spacing、typography、shadow、opacity、borderRadius、borderWidth、fontWeights、fontSizes、fontFamilies、letterSpacing、textDecoration、textCase。(注意 camelCase / 复数形式——不是border-radius或font-size。) - #2 异步应用令牌——这是此技能最关键的失败模式。
shape.applyToken(token, properties)是异步操作(约 100 ms),并且按名称进行绑定(活动集合 / 主题变更时,绑定会重新解析——这正是深色模式能够生效的原因)。在一次execute_code调用中应用,稍后再通过另一次调用验证shape.tokens。绝不要在单次调用中批量应用——在一个循环中执行数百次applyToken会产生竞争,并扰乱绑定;每次调用应用约 25–40 个形状,验证每个批次,并重试遗漏项。文本 / Rectangle 的填充绑定最不稳定(Board 绑定可靠);绑定会保留——应用正确令牌覆盖错误绑定,不要尝试通过fills清除。 - 遇到不熟悉的成员时,先通过
penpot_api_info('TokenSet')/penpot_api_info('Token')验证。
5. 令牌感知型需求说明约定
变更之前,将请求重述为:
- 背景——产品、品牌、现有系统(如有)、浅色 / 深色需求。
- 目标——单一且具体(例如,“创建原始 + 语义颜色 / 间距令牌,并创建深色主题”)。
- 输入——现有令牌、品牌调色板、字号比例(默认小三度 1.2)、间距网格(默认 4px)。
- 约束——不得使用硬编码值;语义令牌必须引用原始令牌;间距必须符合 4px 网格。
- 验收标准——每个令牌都能解析;语义层引用原始令牌;所有间距都是 4px 的倍数;(如有要求)深色主题可正常使用。
以绝不硬编码值的资深设计系统工程师身份行事。
6. 强制工作流
阶段 0 — 发现(只读)。 依次调用 high_level_overview、penpotUtils.tokenOverview(),然后读取
shapeStructure(penpot.currentPage.root, 3)。将现有内容记录到 RUN_ID 台账中。
✋ 检查点:确认范围(新建系统、扩展现有系统,还是从设计推断)。
阶段 1 — 基础变量。 创建 primitives 集合,并将其激活(set.toggleActive() — 新集合默认处于非激活状态),然后添加原始颜色阶梯、间距比例(4px 网格)、圆角比例和字体比例(字体大小采用小三度音阶)。使用 scripts/createTokenSet.js。
✋ 检查点:检查基础变量比例(tokenOverview() + 命名)。
阶段 2 — 语义变量。 添加引用基础变量的意图别名,并将其分配到两个集合中(被引用的 primitives 集合必须处于激活状态,否则引用会验证失败):
- 颜色语义(
color.action.primary.bg→{color.blue.500})→modes/light; - 模式不变语义(
spacing.*、radius.*、font.*)→semantic。
scripts/createSemanticTokens.js 会按变量类型完成拆分。这样才能让暗色模式成为一套清晰的平行颜色集合。✋ 检查点:检查语义映射。
阶段 3 — 暗色模式(可选)。 使用 scripts/createThemes.js 创建平行的 modes/dark 颜色集合(颜色名称与 modes/light 相同,但颜色值适用于暗色模式);让 modes/light 保持激活状态作为默认模式,并将切换操作交给用户。覆盖 modes/light 中的每个颜色名称(脚本会返回 missing 列表)。暗色模式可用——用户激活 modes/dark 时,绑定会按变量名称重新解析——但 Agent 无法通过插件渲染切换操作(theme.addSet() 不会持久化;插件读取存在延迟)。用户可在 Penpot 的 Tokens 面板中开启 modes/dark / 关闭 modes/light(参见 references/04-themes-modes.md)。✋ 检查点:验证暗色模式的 resolvedValue,恢复浅色模式,并告知用户如何切换。
阶段 4 — 应用 / 推断 / 绑定。 可以将变量绑定到选区(scripts/applyTokensToShapes.js)、从硬编码设计中推断变量(scripts/inferTokensFromShapes.js),或者——最重要的是——按精确值匹配,将变量自动绑定到所有尚未绑定的元素,免得用户手动操作(scripts/bindTokensToElements.js)。这也是主题化的前置条件:只有图形绑定到变量名称时,设计才会随浅色 / 暗色切换而变化(集合 / 主题变化时,绑定会按名称重新解析)。分批应用并验证——applyToken 是异步操作,一次调用批量应用会导致竞争条件或绑定混乱;文本 / 矩形的填充绑定不稳定,需要验证并重试(参见 shared/plugin-api-gotchas.md)。仅按值匹配是一种启发式方法:对颜色相反的界面元素,标记出来以便检查其语义角色。✋ 检查点:检查绑定和覆盖率。
阶段 5 — 验证。 运行 scripts/validateTokens.js,检查孤立变量、未解析引用和偏离网格的间距。生成结构化报告。
7. 关键规则
- 基础变量 → 语义变量 → 组件变量。图形应绑定到语义变量,绝不能绑定到基础变量。
- 所有间距 / 内边距 / 间隙 / 圆角值都必须是 4px 的倍数。
- 绝不创建无法说明理由的变量;在检查点提出新变量,不要自动创建。
- 保证幂等:添加前按名称检查是否已有集合 / 变量(避免重复)。
- 字体大小遵循小三度音阶(1.2),除非品牌规范另有要求。
- 不要在同一次
execute_code调用中应用变量后又读取其结果(操作是异步的)。 - 持续更新
RUN_ID台账,以便会话截断后恢复工作。
8. 领域架构
三个令牌层级(参见 shared/tokens-schema.json):
- primitives — 原始且不依赖上下文的刻度(
color.blue.500、spacing.4、radius.md、font.size.300)。存在语义令牌时,绝不直接绑定到形状。 - semantic — 引用 primitives 的意图别名(
color.text.default→{color.gray.900}、spacing.inset.md→{spacing.4})。形状绑定的就是这一层。 - components — 可选的组件级覆盖项,引用 semantic 令牌。仅在组件需要专属表面样式时创建。
集合布局(参见 shared/modes-and-policies.md):模式不变的语义放在 semantic 中;颜色
语义放在 modes/light / modes/dark 中(两者使用相同名称)。主题建模为 Penpot 令牌
主题(addTheme({ group, name })),用于切换匹配的 modes/* 集合;primitives 和
模式不变的 semantic 集合保持不变。
9. 模式与策略
默认模式为 review。创建新令牌、构建主题或将令牌应用到形状,都需要检查点。唯一可自动应用的操作(安全集合,shared/modes-and-policies.md)是:将一个原始值替换为某个令牌,且该值必须与该令牌解析后的值完全相同。
10. 状态管理
RUN_ID 下的记录键(参见 shared/state-management.md):phase、created:[{kind:'set'|'token'|'theme', name, id}]、proposed:[...]、applied:[...]。截断后,继续操作前先使用 tokenOverview() 重新推导实际状态。
11. 用户检查点
| 阶段之后 | 展示的内容 | 询问事项 |
|---|---|---|
| 0 Discovery | 现有令牌摘要 | 新建、扩展还是推断? |
| 1 Primitives | primitives 的 tokenOverview() | 批准刻度和命名? |
| 2 Semantic | semantic→primitive 映射 | 批准意图映射? |
| 3 Themes | 各主题下的解析值 | 批准主题切换? |
| 4 Apply/Infer | 示例的前后对比 | 批准绑定? |
12. 命名约定
遵循 shared/naming-conventions.md 使用点分隔命名:集合为 primitives / semantic(模式不变)/
modes/light + modes/dark(颜色)/ 可选的 components;令牌使用小写点分隔路径;主题
归入 mode 组,名称为 Light / Dark / High Contrast。
13. 防止自我合理化对照表
| 大模型的借口 | 错误原因 | 确定性应对措施 |
|---|---|---|
| “我先硬编码这个十六进制值,以后再改成令牌。” | 硬编码值会破坏主题适配和治理;“以后”永远不会到来。 | 停下。读取 tokenOverview(),绑定到语义令牌,或提交一个令牌供审核。 |
| “我直接把形状绑定到 primitive,省一步。” | Primitives 无法按主题切换;深色模式会出问题。 | 绑定到引用该 primitive 的语义令牌。 |
| “这个间距是 18px,差不多就行。” | 偏离网格的值会破坏节奏并违反治理要求。 | 四舍五入到最接近的 4px 网格令牌(16 或 20)。任何例外都必须经人工批准并记录。 |
| “我先创建十几个令牌,以防万一。” | 未使用或孤立的令牌会形成负担。 | 只创建当前所需的令牌;其余令牌在检查点提交。 |
14. 辅助代码片段
// Idempotent set + token creation (Phase 1)
const tok = penpot.library.local.tokens;
let prim = tok.sets.find(s => s.name === "primitives") || tok.addSet({ name: "primitives" });
if (!prim.active) prim.toggleActive(); // new sets are INACTIVE by default
if (!prim.tokens.find(t => t.name === "color.blue.500")) {
prim.addToken({ type: "color", name: "color.blue.500", value: "#0066FF" });
}
return { set: prim.name, count: prim.tokens.length };
// Semantic alias referencing a primitive (Phase 2)
const tok = penpot.library.local.tokens;
let sem = tok.sets.find(s => s.name === "semantic") || tok.addSet({ name: "semantic" });
if (!sem.active) sem.toggleActive(); // and the referenced 'primitives' set must be active too
if (!sem.tokens.find(t => t.name === "color.action.primary.bg")) {
sem.addToken({ type: "color", name: "color.action.primary.bg", value: "{color.blue.500}" });
}
return { ok: true };
15. 参考资源
penpot_api_info('TokenCatalog')、penpot_api_info('TokenSet')、penpot_api_info('Token')。shared/tokens-schema.json(分类体系 + 实际类型字符串)、shared/plugin-api-gotchas.md。
16. 支持文件
references/
| 文件 | 加载用途 |
|---|---|
01-discovery.md | 阶段 0 只读清点 |
02-token-architecture.md | 层级、比例和类型比例 |
03-infer-from-design.md | 从硬编码设计中构建令牌 |
04-themes-modes.md | 浅色/深色主题建模 |
05-naming.md | 令牌命名细则 |
06-anti-rationalization.md | 治理借口及应对措施 |
07-error-recovery.md | 常见故障及修复方法 |
scripts/
| 文件 | 用途 |
|---|---|
inspectFileStructure.js | 读取页面/库/令牌 |
createTokenSet.js | 幂等地创建基础令牌集和令牌 |
createSemanticTokens.js | 创建引用基础令牌的语义别名 |
inferTokensFromShapes.js | 从原始值推导令牌 |
applyTokensToShapes.js | 将令牌绑定到选区 |
bindTokensToElements.js | 按值将令牌自动绑定到未绑定的元素(分块执行并验证)——支持主题化 |
createThemes.js | 浅色/深色主题 |
validateTokens.js | 检查孤立令牌、未解析的引用和偏离网格的值 |
规则路径。 在原生安装中,shared/… 和 policies/… 会在此捆绑包内解析(由安装程序随附);在 Claude Code 插件安装中,它们位于插件根目录——${CLAUDE_PLUGIN_ROOT}/shared/…,即本文件向上两级。