penpot-foundations について
Penpotでデザイントークンの基盤を作成・管理するスキルで、既存デザインからのトークン推定にも対応します。トークンをプリミティブ、セマンティック、任意のコンポーネント層に分け、参照、バインド、間隔を段階的に検証します。
主な機能
- 01プリミティブとセマンティックの層を作成
- 02既存デザインからトークンを推定
- 03図形をセマンティックトークンにバインド
- 04ライト・ダークのカラーモードを設定
- 05参照関係と間隔を検証
活用シーン
- 01デザインシステムのトークンを整備
- 02ハードコードされたデザインからトークンを抽出
- 03既存図形をバインドしてテーマ切り替え
- 04トークン参照と間隔ルールを確認
MCP 対応
`execute_code` を通じて Penpot MCP ツールを使用します。`penpotUtils.tokenOverview`、`shapeStructure`、トークンの作成・適用 API が含まれます。
インストール
コマンドをコピーしてターミナルで実行します。
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) でシェイプに適用します。3段階(プリミティブ → セマンティック → コンポーネント)で機能し(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})はオブジェクト引数を取ります。新しいセットは非アクティブです(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。(キャメルケースや複数形に注意してください。border-radiusやfont-sizeではありません。)- #2 非同期のトークン適用 — このスキルで最も重大な失敗要因です。
shape.applyToken(token, properties)は非同期(約 100 ms)で、名前によってバインドします(アクティブなセット/テーマが変わるとバインディングが再解決されます。これによってダークモードが機能します)。1 回のexecute_code呼び出しで適用し、後続の呼び出しでshape.tokensを検証します。1 回の呼び出しで大量に適用してはいけません。 1 つのループで数百のapplyTokenを実行すると競合が発生し、バインディングが乱れます。1 回の呼び出しにつき約 25~40 個のシェイプに分けて適用し、各チャンクを検証して、適用漏れがあれば再試行します。テキスト/長方形の塗りのバインディングは特に不安定です(ボードは安定しています)。バインディングは保持されます。誤ったバインディングは正しいトークンを適用して上書きし、fills経由でクリアしようとしないでください。 - 不慣れなメンバーは、まず
penpot_api_info('TokenSet')/penpot_api_info('Token')で確認してください。
5. トークン対応ブリーフの契約
変更を加える前に、依頼内容を次のように言い換えます:
- コンテキスト — 製品、ブランド、既存のシステム(あれば)、ライト/ダークの要件。
- 目的 — 単一かつ具体的な目的(例:「プリミティブとセマンティックの色/間隔トークン、およびダークテーマを作成する」)。
- 入力 — 既存のトークン、ブランドパレット、文字サイズスケールの比率(デフォルトは Minor Third 1.2)、間隔グリッド(デフォルトは 4px)。
- 制約 — ハードコードされた値を使用しないこと。セマンティックトークンはプリミティブを参照すること。間隔は 4px グリッドに合わせること。
- 受け入れ基準 — すべてのトークンが解決されること。セマンティック層がプリミティブを参照すること。すべての間隔が 4px の倍数であること。(依頼された場合)ダークテーマが機能すること。
値を決してハードコードしない、シニアのデザインシステムエンジニアとして行動してください。
6. 必須ワークフロー
フェーズ 0 — 調査(読み取り専用)。 high_level_overview、続いて penpotUtils.tokenOverview() と
shapeStructure(penpot.currentPage.root, 3) の読み取りを実行します。既存の内容を RUN_ID 台帳に記録します。
✋ チェックポイント:対象範囲(新規システム、拡張、デザインからの推測)を確認します。
フェーズ 1 — プリミティブ。 primitives セットを作成して有効化し(新しいセットは無効状態で作成されます)、生のカラーランプ、スペーシングスケール(4px グリッド)、角丸スケール、タイポグラフィスケールを追加します(フォントサイズには短三度を使用します)。scripts/createTokenSet.js を使用します。
✋ チェックポイント:プリミティブのスケールを確認します(tokenOverview() と命名)。
フェーズ 2 — セマンティック。 プリミティブを参照する意図別のエイリアスを追加し、2 つのセットに振り分けます(参照先の primitives セットが有効でない場合、参照の検証に失敗します)。
- 色のセマンティクス(
color.action.primary.bg→{color.blue.500})→modes/light; - モードに依存しないセマンティクス(
spacing.*、radius.*、font.*)→semantic。
scripts/createSemanticTokens.js はトークンの種類に応じてこの振り分けを行います。これにより、ダークモードをクリーンな並行カラーセットとして実現できます。✋ チェックポイント:セマンティックマッピングを確認します。
フェーズ 3 — ダークモード(任意)。 scripts/createThemes.js で、modes/light と同じ色名とダークカラー値を持つ並行した modes/dark カラーセットを作成します。デフォルトとして modes/light を有効のままにし、切り替え方法をユーザーに伝えます。modes/light の色名をすべて網羅します(スクリプトは missing リストを返します)。ダークモードは機能します。ユーザーが modes/dark を有効にすると、バインディングはトークンの名前によって再解決されます。ただし、エージェントはプラグインから切り替えを描画できません(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. ドメインアーキテクチャ
3 つのトークン階層(shared/tokens-schema.json を参照):
- プリミティブ — コンテキストに依存しない生のスケールです(
color.blue.500、spacing.4、radius.md、font.size.300)。セマンティックトークンが存在する場合、シェイプに直接割り当ててはいけません。 - セマンティック — プリミティブを参照する意図のエイリアスです(
color.text.default→{color.gray.900}、spacing.inset.md→{spacing.4})。シェイプが割り当てるのはこれです。 - コンポーネント — セマンティックトークンを参照する、コンポーネントごとの任意のオーバーライドです。コンポーネント独自のサーフェスが必要な場合にのみ作成します。
セットの構成(shared/modes-and-policies.md を参照):モードに依存しないセマンティクスは semantic に置きます。色の
セマンティクスは modes/light / modes/dark に置きます(両方で同じ名前を使用)。テーマは Penpot トークン
テーマ(addTheme({ group, name }))としてモデル化し、対応する modes/* セットを切り替えます。プリミティブと
モードに依存しない semantic セットは一定のままです。
9. モードとポリシー
デフォルトは レビュー です。新しいトークンの作成、テーマの構築、シェイプへのトークンの適用には、すべてチェックポイントが必要です。自動適用できる唯一のアクション(安全なセットは 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 調査 | 既存トークンの概要 | 新規作成/拡張/推定のどれにしますか? |
| 1 プリミティブ | プリミティブの tokenOverview() | スケールと命名を承認しますか? |
| 2 セマンティック | セマンティックからプリミティブへのマッピング | 意図のマッピングを承認しますか? |
| 3 テーマ | テーマごとの解決済み値 | テーマの切り替えを承認しますか? |
| 4 適用/推定 | サンプルの適用前/適用後 | バインディングを承認しますか? |
12. 命名規則
shared/naming-conventions.md に従ったドット記法:セットは primitives / semantic(モードに依存しない)/
modes/light + modes/dark(色)/ 任意の components とします。トークンは小文字のドットパスとし、テーマは
mode グループに Light / Dark / High Contrast という名前でまとめます。
13. 合理化を防ぐための表
| LLM がする言い訳 | 間違っている理由 | 確実な対策 |
|---|---|---|
| 「今はこの 16 進数をハードコードして、後でトークン化します。」 | ハードコードされた値はテーマ設定とガバナンスを損ないます。「後で」は決して訪れません。 | そこで止めます。tokenOverview() を読み、セマンティックトークンを割り当てるか、レビュー用に提案します。 |
| 「手間を省くため、シェイプをプリミティブに直接割り当てます。」 | プリミティブはテーマごとに切り替えられず、ダークモードが崩れます。 | プリミティブを参照するセマンティックトークンを割り当てます。 |
| 「この間隔は 18px ですが、十分近いです。」 | グリッドから外れた値はリズムを損ない、ガバナンスに適合しません。 | 最も近い 4px グリッドのトークン(16 または 20)に丸めます。例外は人間の承認を得た場合にのみ記録します。 |
| 「念のため、最初にトークンを 12 個作成します。」 | 未使用または孤立したトークンは負債になります。 | 今必要なものだけを作成し、残りはチェックポイントで提案します。 |
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 | 孤立したトークン、未解決の参照、グリッドからの逸脱 |
Doctrine のパス。 shared/… と policies/… は、ネイティブインストールではこのバンドル内に解決されます(インストーラーによってベンダー化されます)。Claude Code プラグインのインストールでは、このファイルから 2 つ上のディレクトリにあるプラグインルート、${CLAUDE_PLUGIN_ROOT}/shared/… に置かれます。