penpot-foundations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesepenpot-foundations — token & library foundation
penpot-foundations — token 与库基础
1. Title + How it works
1. 标题与工作原理
penpot-foundationsexecute_codeexport_shapepenpotUtils.shapeStructureshared/penpot-mcp-tool-reference.mdpenpotUtils.tokenOverview()penpot.library.local.tokensaddSetset.addToken({type,name,value})shape.applyToken(token, properties)shared/tokens-schema.jsonpenpot-foundationsexecute_codeexport_shapepenpotUtils.shapeStructureshared/penpot-mcp-tool-reference.mdpenpotUtils.tokenOverview()penpot.library.local.tokensaddSetset.addToken({type,name,value})shape.applyToken(token, properties)shared/tokens-schema.json2. The One Rule That Matters Most
2. 最重要的核心规则
Tokens before everything, and never one-shot. Build the foundation in small, idempotent steps:
primitives first, then semantic aliases that reference them, then (only if needed) component tokens.
Validate after each tier with . Never invent values silently — propose the scale and
let a human approve it at the checkpoint.
tokenOverview()Token优先于一切,且绝不能一次性完成。 以小型、幂等的步骤构建基础:先做基础token,再做引用它们的语义别名,最后(仅在需要时)做组件token。每完成一层都用 验证。绝不能凭空创造值——提出刻度方案,由人工在检查点确认。
tokenOverview()3. Penpot MCP Tool Reference
3. Penpot MCP 工具参考
Full surface: . Domain calls this skill leans on:
/ (discovery + inference);
/ / (writes);
before relying on member signatures.
shared/penpot-mcp-tool-reference.mdpenpotUtils.tokenOverview()shapeStructure()penpot.library.local.tokens.addSetset.addTokenshape.applyTokenpenpot_api_info('TokenSet'|'Token'|'TokenCatalog')完整接口说明见 。本技能主要依赖的领域调用:
/ (发现与推断);
/ / (写入操作);
在使用成员签名前,可调用 查看详情。
shared/penpot-mcp-tool-reference.mdpenpotUtils.tokenOverview()shapeStructure()penpot.library.local.tokens.addSetset.addTokenshape.applyTokenpenpot_api_info('TokenSet'|'Token'|'TokenCatalog')4. Plugin API Essentials
4. 插件 API 核心要点
Gotcha numbers refer to .
shared/plugin-api-gotchas.md- #8 token creation — ;
penpot.library.local.tokens/addSet({name})take OBJECT args; new sets are INACTIVE (addTheme({group,name})); tokenset.toggleActive()is always a string (literal orvalue); read"{reference}".token.resolvedValue - Use the exact token strings from
type:shared/tokens-schema.json,color,dimension,spacing,typography,shadow,opacity,borderRadius,borderWidth,fontWeights,fontSizes,fontFamilies,letterSpacing,textDecoration. (Note camelCase/plural — NOTtextCaseorborder-radius.)font-size - #2 async token application — this skill's critical failure mode. is async (~100 ms) and binds by NAME (bindings re-resolve when the active set/theme changes — that is what makes dark mode work). Apply in one
shape.applyToken(token, properties)call, verifyexecute_codein a LATER call. Never mass-apply in a single call — hundreds ofshape.tokenss in one loop race and scramble bindings; apply in chunks of ≈25–40 shapes per call, verify each chunk, retry misses. Text/Rectangle fill bindings are the flakiest (Boards are reliable); bindings are sticky — overwrite a wrong one by applying the correct token, don't try to clear it viaapplyToken.fills - Verify any unfamiliar member with /
penpot_api_info('TokenSet')first.penpot_api_info('Token')
注意事项编号对应 文件。
shared/plugin-api-gotchas.md- #8 token 创建 — 通过 操作;
penpot.library.local.tokens/addSet({name})接收对象参数;新创建的集合默认处于未激活状态(需调用addTheme({group,name})激活);token 的set.toggleActive()始终为字符串(字面量或value引用格式);可读取"{reference}"获取解析后的值。token.resolvedValue - 请使用 中定义的精确token
shared/tokens-schema.json字符串:type、color、dimension、spacing、typography、shadow、opacity、borderRadius、borderWidth、fontWeights、fontSizes、fontFamilies、letterSpacing、textDecoration。(注意是驼峰命名/复数形式——不是textCase或border-radius。)font-size - #2 异步token应用——本技能的关键故障点。 是异步操作(约100毫秒),且按名称绑定(当激活的集合/主题变化时,绑定会重新解析——这是深色模式生效的原理)。请在一次
shape.applyToken(token, properties)调用中完成应用,在后续调用中验证execute_code。绝不要在单次调用中批量应用——循环中执行数百个shape.tokens会引发竞态,导致绑定混乱;请按每次调用约25-40个形状的批次应用,每批验证后重试失败项。文本/矩形的填充绑定最不稳定(画板绑定是可靠的);绑定是持久的——如果绑定错误,直接应用正确的token覆盖即可,不要尝试通过applyToken清除。fills - 遇到不熟悉的成员,请先调用 /
penpot_api_info('TokenSet')确认。penpot_api_info('Token')
5. Token-Aware Brief Contract
5. 具备Token意识的需求约定
Before mutating, restate the request as:
- Context — product, brand, existing system (if any), light/dark needs.
- Objective — single and specific (e.g., "create primitive + semantic color/spacing tokens and a dark theme").
- Inputs — existing tokens, brand palette, type scale ratio (default Minor Third 1.2), spacing grid (default 4px).
- Constraints — no hardcoded values; semantic tokens must reference primitives; spacing on the 4px grid.
- Acceptance Criteria — every token resolves; semantic tier references primitives; all spacing is a 4px multiple; (if requested) a working dark theme.
Act as a senior design-systems engineer who never hardcodes values.
在进行变更前,请将需求重述为以下部分:
- 上下文 — 产品、品牌、现有系统(如有)、浅色/深色模式需求。
- 目标 — 单一且明确(例如:"创建基础+语义颜色/间距token和深色主题")。
- 输入 — 现有token、品牌调色板、字号比例(默认小三度 1.2)、间距网格(默认4px)。
- 约束 — 不允许硬编码值;语义token必须引用基础token;间距遵循4px网格。
- 验收标准 — 所有token都能解析;语义层引用基础层;所有间距都是4px的倍数;(如要求)深色主题可正常工作。
请扮演从不硬编码值的资深设计系统工程师。
6. Mandatory Workflow
6. 强制工作流
Phase 0 — Discovery (read-only). , then and a
read. Record what already exists in the ledger.
✋ Checkpoint: confirm scope (new system vs. extend vs. infer-from-design).
high_level_overviewpenpotUtils.tokenOverview()shapeStructure(penpot.currentPage.root, 3)RUN_IDPhase 1 — Primitives. Create the set and activate it ( — new
sets are created inactive), then add raw color ramps, the spacing scale (4px grid), radius scale, and
type scale (font sizes via Minor Third). Use .
✋ Checkpoint: review the primitive scales ( + naming).
primitivesset.toggleActive()scripts/createTokenSet.jstokenOverview()Phase 2 — Semantic. Add intent aliases that reference primitives, routed into two sets (the
referenced set must be active or references fail validation):
primitives- colour semantics (→
color.action.primary.bg) →{color.blue.500};modes/light - mode-invariant semantics (,
spacing.*,radius.*) →font.*.semantic
scripts/createSemanticTokens.jsPhase 3 — Dark mode (optional). Create the parallel colour set (same colour names
as , dark values) with ; leave active as default
and hand the toggle to the user. Cover every colour name (the script returns a
list). Dark mode works — bindings re-resolve by token name when the user activates
— but the agent cannot render the switch from the plugin ( doesn't persist;
the plugin's read lags). The user toggles on / off in Penpot's Tokens panel
(see ). ✋ Checkpoint: verify dark s, restore light, and
tell the user how to switch.
modes/darkmodes/lightscripts/createThemes.jsmodes/lightmodes/lightmissingmodes/darktheme.addSet()modes/darkmodes/lightreferences/04-themes-modes.mdresolvedValuePhase 4 — Apply / Infer / Bind. Either bind tokens to a selection (),
infer tokens from a hardcoded design (), or — fundamental —
auto-bind tokens to every unbound element by exact value match so the user doesn't have to do it by
hand (). This is also the precondition for theming: a design only
follows a light/dark switch if its shapes are bound to token names (bindings re-resolve by name when
sets/themes change). Apply in chunks and verify — is async and mass-application in one
call races/scrambles bindings; Text/Rectangle fill bindings are flaky and need verify+retry (see
). Value-only matches are a heuristic: flag inverse-colored chrome for a
semantic-role review. ✋ Checkpoint: review bindings + coverage.
scripts/applyTokensToShapes.jsscripts/inferTokensFromShapes.jsscripts/bindTokensToElements.jsapplyTokenshared/plugin-api-gotchas.mdPhase 5 — Validate. — orphans, unresolved references, off-grid spacing.
Produce the structured report.
scripts/validateTokens.js阶段0 — 发现(只读)。 先执行 ,然后运行 和 读取结构。在 台账中记录已有的内容。
✋ 检查点:确认范围(新建系统 / 扩展现有系统 / 从设计推断)。
high_level_overviewpenpotUtils.tokenOverview()shapeStructure(penpot.currentPage.root, 3)RUN_ID阶段1 — 基础token。 创建 集合并激活它(调用 —— 新集合默认未激活),然后添加原始色阶、间距刻度(4px网格)、圆角刻度和字号刻度(按小三度比例的字号)。使用 脚本。
✋ 检查点:审核基础刻度( + 命名)。
primitivesset.toggleActive()scripts/createTokenSet.jstokenOverview()阶段2 — 语义token。 添加引用基础token的意图别名,分为两个集合(被引用的 集合必须处于激活状态,否则引用会验证失败):
primitives- 颜色语义(→
color.action.primary.bg)→ 存入{color.blue.500};modes/light - 模式无关语义(、
spacing.*、radius.*)→ 存入font.*。semantic
scripts/createSemanticTokens.js阶段3 — 深色模式(可选)。 使用 创建平行的 颜色集合(颜色名称与 完全一致,值为深色版本);保持 为默认激活状态,将切换权交给用户。需覆盖所有 中的颜色名称(脚本会返回 缺失列表)。深色模式可以正常工作——当用户激活 时,绑定会按token名称重新解析——但Agent无法在插件中渲染切换效果( 不会持久化;插件的读取会有延迟)。用户需要在Penpot的Token面板中手动开启 / 关闭 (见 )。
✋ 检查点:验证深色模式的 ,恢复浅色模式,并告知用户切换方法。
scripts/createThemes.jsmodes/darkmodes/lightmodes/lightmodes/lightmissingmodes/darktheme.addSet()modes/darkmodes/lightreferences/04-themes-modes.mdresolvedValue阶段4 — 应用/推断/绑定。 可以将token绑定到选中内容(),从硬编码设计推断token(),或者——核心功能——通过精确值匹配自动将token绑定到所有未绑定元素,免去用户手动操作()。这也是主题化的前提:只有当设计中的形状都绑定到token名称时,设计才能跟随浅色/深色切换变化(集合/主题变化时,绑定会按名称重新解析)。请分批应用并验证—— 是异步操作,单次调用中批量应用会引发竞态/绑定混乱;文本/矩形的填充绑定不稳定,需要验证+重试(见 )。仅按值匹配是启发式方法:需标记反色的界面控件,供语义角色审核。
✋ 检查点:审核绑定情况与覆盖率。
scripts/applyTokensToShapes.jsscripts/inferTokensFromShapes.jsscripts/bindTokensToElements.jsapplyTokenshared/plugin-api-gotchas.md阶段5 — 验证。 运行 —— 检查孤立token、未解析引用、偏离网格的间距。生成结构化报告。
scripts/validateTokens.js7. Critical Rules
7. 关键规则
- Primitives → semantic → component. Shapes bind to semantic tokens, never primitives.
- All spacing/padding/gap/radius values are multiples of 4px.
- Never create a token you can't justify; propose new tokens at a checkpoint, don't auto-create them.
- Idempotent: check for an existing set/token by name before adding (no duplicates).
- Type sizes follow a Minor Third (1.2) scale unless the brand dictates otherwise.
- Do not apply and read back a token in the same call (async).
execute_code - Keep the ledger updated so a truncated session can resume.
RUN_ID
- 基础层 → 语义层 → 组件层。形状绑定到语义token,绝不直接绑定基础token。
- 所有间距/内边距/间隙/圆角值都是4px的倍数。
- 绝不创建无法说明用途的token;在检查点提出新token建议,不要自动创建。
- 幂等性:添加前先检查是否已存在同名集合/token(避免重复)。
- 除非品牌有特殊要求,字号遵循小三度(1.2)比例。
- 不要在同一次 调用中既应用token又回读结果(异步特性)。
execute_code - 保持 台账更新,以便会话中断后可以恢复。
RUN_ID
8. Domain Architecture
8. 领域架构
Three token tiers (see ):
shared/tokens-schema.json- primitives — raw, context-free scale (,
color.blue.500,spacing.4,radius.md). Never bound directly to shapes when a semantic token exists.font.size.300 - semantic — intent aliases referencing primitives (→
color.text.default,{color.gray.900}→spacing.inset.md). This is what shapes bind to.{spacing.4} - components — optional per-component overrides referencing semantic tokens. Create only when a component needs its own surface.
Set layout (see ): mode-invariant semantics live in ; colour
semantics live in / (same names in both). Themes are modeled as Penpot token
themes () that toggle the matching set; primitives and the
mode-invariant set stay constant.
shared/modes-and-policies.mdsemanticmodes/lightmodes/darkaddTheme({ group, name })modes/*semantic三层token架构(见 ):
shared/tokens-schema.json- 基础层(primitives) — 原始的、无上下文的刻度(、
color.blue.500、spacing.4、radius.md)。当存在语义token时,绝不要直接绑定到形状。font.size.300 - 语义层(semantic) — 引用基础token的意图别名(→
color.text.default、{color.gray.900}→spacing.inset.md)。形状绑定的就是这一层。{spacing.4} - 组件层(components) — 可选的、引用语义token的单组件覆盖值。仅当组件需要专属表层时创建。
集合布局(见 ):模式无关的语义token存放在 集合中;颜色语义token存放在 / 集合中(两者名称完全一致)。主题以Penpot token主题的形式实现(),用于切换对应的 集合;基础集合和模式无关的 集合保持不变。
shared/modes-and-policies.mdsemanticmodes/lightmodes/darkaddTheme({ group, name })modes/*semantic9. Modes & Policies
9. 模式与策略
Default review. Creating a new token, building a theme, or applying tokens to shapes all require a
checkpoint. The only auto-applicable action (safe set, ) is replacing a
raw value that is exactly equal to an existing token's resolved value with that token.
shared/modes-and-policies.md默认采用审核模式。创建新token、构建主题或将token应用到形状都需要经过检查点。唯一可自动执行的操作(安全操作,见 )是:将与现有token解析后值完全相等的原始值替换为该token。
shared/modes-and-policies.md10. State Management
10. 状态管理
Ledger keys under (see ): , , , . Re-derive reality with after truncation before continuing.
RUN_IDshared/state-management.mdphasecreated:[{kind:'set'|'token'|'theme', name, id}]proposed:[...]applied:[...]tokenOverview()RUN_IDshared/state-management.mdphasecreated:[{kind:'set'|'token'|'theme', name, id}]proposed:[...]applied:[...]tokenOverview()11. User Checkpoints
11. 用户检查点
| After phase | Artifacts shown | What we ask |
|---|---|---|
| 0 Discovery | existing tokens summary | New / extend / infer? |
| 1 Primitives | | Approve scales & naming? |
| 2 Semantic | semantic→primitive mapping | Approve intent mapping? |
| 3 Themes | resolved values per theme | Approve theme switch? |
| 4 Apply/Infer | before/after on a sample | Approve bindings? |
| 阶段完成后 | 展示产物 | 我们确认的内容 |
|---|---|---|
| 0 发现阶段 | 现有token汇总 | 新建 / 扩展 / 推断? |
| 1 基础token阶段 | 基础token的 | 是否认可刻度与命名? |
| 2 语义token阶段 | 语义→基础映射关系 | 是否认可意图映射? |
| 3 主题阶段 | 各主题的解析后值 | 是否认可主题切换? |
| 4 应用/推断阶段 | 样本的前后对比 | 是否认可绑定结果? |
12. Naming Conventions
12. 命名规范
Dot-notation per : sets are / (mode-invariant) /
+ (colour) / optional ; tokens are lowercase dot paths; themes
grouped with names / / .
shared/naming-conventions.mdprimitivessemanticmodes/lightmodes/darkcomponentsmodeLightDarkHigh Contrast遵循 的点分命名法:集合名为 / (模式无关) / + (颜色) / 可选的 ;token为小写点路径;主题分组为 ,名称为 / / 。
shared/naming-conventions.mdprimitivessemanticmodes/lightmodes/darkcomponentsmodeLightDarkHigh Contrast13. Anti-Rationalization Table
13. 反合理化对照表
| Excuse the LLM makes | Why it's wrong | Deterministic countermeasure |
|---|---|---|
| "I'll hardcode this hex now and tokenize later." | Hardcoded values break theming and governance; "later" never comes. | Stop. Read |
| "I'll bind shapes straight to a primitive to save a step." | Primitives can't switch per theme; dark mode breaks. | Bind to a semantic token that references the primitive. |
| "This spacing is 18px, close enough." | Off-grid values erode rhythm and fail governance. | Round to the nearest 4px-grid token (16 or 20). Document any exception only with human approval. |
| "I'll create a dozen tokens up front to be safe." | Unused/orphan tokens are debt. | Create only what's needed now; propose the rest at a checkpoint. |
| LLM常找的借口 | 错误原因 | 确定性应对措施 |
|---|---|---|
| "我先硬编码这个色值,之后再token化。" | 硬编码值会破坏主题化和治理;"之后"永远不会来。 | 停止操作。读取 |
| "我直接把形状绑定到基础token,省一步。" | 基础token不能按主题切换;深色模式会失效。 | 绑定到引用该基础token的语义token。 |
| "这个间距是18px,差不多就行。" | 偏离网格的值会破坏节奏,且不符合治理要求。 | 四舍五入到最近的4px网格token(16或20)。所有例外都需经人工批准并记录。 |
| "我先创建十几个token备用,更稳妥。" | 未使用的/孤立的token是技术债务。 | 只创建当前需要的内容;其余的在检查点提出建议。 |
14. Helper Code Snippets
14. 辅助代码片段
js
// 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 };js
// 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 };js
// 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 };js
// 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. Reference Resources
15. 参考资源
- ,
penpot_api_info('TokenCatalog'),penpot_api_info('TokenSet').penpot_api_info('Token') - (taxonomy + real type strings),
shared/tokens-schema.json.shared/plugin-api-gotchas.md
- 、
penpot_api_info('TokenCatalog')、penpot_api_info('TokenSet')。penpot_api_info('Token') - (分类体系 + 实际类型字符串)、
shared/tokens-schema.json。shared/plugin-api-gotchas.md
16. Supporting Files
16. 配套文件
references/
| File | Loaded for |
|---|---|
| Phase 0 read-only inventory |
| tiers, scales, type ratio |
| building tokens from a hardcoded design |
| light/dark theme modeling |
| token naming specifics |
| governance excuses + countermeasures |
| common failures & fixes |
scripts/
| File | Purpose |
|---|---|
| read pages/library/tokens |
| idempotent primitives set + tokens |
| semantic aliases referencing primitives |
| propose tokens from raw values |
| bind tokens to a selection |
| auto-bind tokens to unbound elements by value (chunked + verify) — enables theming |
| light/dark themes |
| orphans, unresolved refs, off-grid |
Doctrine paths. and resolve inside this bundle in native installs (vendored by the installer); in a Claude Code plugin install they live at the plugin root — , two directories up from this file.
shared/…policies/…${CLAUDE_PLUGIN_ROOT}/shared/…references/
| 文件 | 加载用途 |
|---|---|
| 阶段0 只读清单 |
| 层级、刻度、字号比例 |
| 从硬编码设计构建token |
| 浅色/深色主题建模 |
| token命名细节 |
| 治理借口与应对措施 |
| 常见故障与修复方法 |
scripts/
| 文件 | 用途 |
|---|---|
| 读取页面/库/token |
| 幂等创建基础token集合与token |
| 创建引用基础token的语义别名 |
| 从原始值提出token建议 |
| 将token绑定到选中内容 |
| 按值自动绑定token到未绑定元素(分批+验证)—— 实现主题化 |
| 浅色/深色主题 |
| 检查孤立token、未解析引用、偏离网格 |
规则文件路径:原生安装时, 和 位于本包内部(由安装程序内置);在 Claude Code 插件安装场景下,它们位于插件根目录——,即本文件的上两级目录。
shared/…policies/…${CLAUDE_PLUGIN_ROOT}/shared/…