penpot-build-from-code について
penpot-build-from-codeは、JSX、HTML、Vue/Svelteテンプレートやスタイルを編集可能なPenpot画面に変換するスキルです。既存のデザインシステムを使うデザイナーや開発者向けに、トークンとコンポーネントを先に調査し、画面を区分ごとに構築・検証します。
主な機能
- 01デザイントークンとコンポーネントの調査
- 02画面を区分ごとに段階的に構築
- 03スタイルをセマンティックトークンに適用
- 04既存コンポーネントのインスタンスを優先
- 05画面のエクスポートと構造確認
- 06トークン適用率と未対応値の検証
活用シーン
- 01ReactページをPenpot画面として再構築
- 02HTMLやVueテンプレートを既存システムに対応
- 03新しい画面の未適用スタイル値を確認
MCP 対応
Penpot MCPツールでトークン、コンポーネント、画面構造を調べ、画面を作成・変更します。画面をエクスポートして視覚的にも確認します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ penpot/ penpot-ai-kit --skill penpot-build-from-codeインストール後、Claude Code で/penpot-build-from-codeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/01-discovery.md02-component-assembly.md03-token-binding.md04-incremental-transform.md05-anti-rationalization.md06-error-recovery.mdscripts/buildSection.jscreateScreenWrapper.jsinspectDesignSystem.jsvalidateScreen.js詳細
コードから構築 — アプリのコードをシステム上の Penpot 画面に変換する
1. タイトル + 仕組み
このスキルでは、既存のアプリケーション成果物(JSX/TSX、HTML、Vue/Svelte テンプレート、CSS/Tailwind、または styled-components ツリー)を、編集可能でシステムに紐づいた Penpot 画面として再構築します。すべての変更は execute_code を通じて行い、export_shape で視覚的に検証します。構造は penpotUtils.shapeStructure で確認できます(ツール全体のリファレンス:shared/penpot-mcp-tool-reference.md)。重要なのは、まず検出すること(Phase 0 で、マッピング対象となる既存のトークンとコンポーネントを列挙)です。その後、コードをセクションごとに段階的に変換し、各 CSS/スタイル値をセマンティックトークンに、繰り返し使われる UI パーツを既存のライブラリコンポーネントに対応付けます。16 進数カラーやグリッドに沿わない間隔をハードコードしたり、コンポーネントを単なるボックスとして作り直したりしてはいけません。
2. 最も重要なルール
画面を一度に作成してはいけません。また、トークンで表現できる値をハードコードしてはいけません。 画面は一度に1つのセクションずつ構築し(呼び出しごとに、論理的な execute_code ステップを1つ実行)、塗り、間隔、角丸、文字スタイルを設定する前に、検出済みのトークンセットを照合してください。コードから得られた #0066FF は塗りの値ではなく、デザインシステム内で検索するための値です。一致するトークンがなければ、トークンを提案して停止します。独自に作成してはいけません。ページ全体を1回の呼び出しで変換したり、計算済み CSS をそのまま図形に貼り付けたりするのは、このスキルが防ごうとしている失敗パターンです。
3. Penpot MCP ツールリファレンス
ツール全体のリファレンス:shared/penpot-mcp-tool-reference.md。このスキルで使用する呼び出し:
- 検出(読み取り専用):
penpotUtils.tokenOverview()、penpot.library.local(コンポーネント、トークン)、penpotUtils.shapeStructure(shape, depth)、penpotUtils.findShape/findShapes。 - 画面の骨組み:
penpot.createBoard()、board.addFlexLayout()、penpotUtils.addFlexLayout(container, dir)。 - セクションの構築:
penpot.createRectangle/createText/createEllipse/createPath、component.instance()、container.appendChild(child)、child.layoutChild。 - トークンの適用:
shape.applyToken(token, properties[])、token.resolvedValue、penpotUtils.findTokenByName。 - 検証:
export_shape(別のツール呼び出し)とshapeStructureの読み取り。
4. プラグイン API の基本事項
このスキルで問題になりやすい注意点を一行ずつまとめています。全文は shared/plugin-api-gotchas.md を参照してください。
- #1 スタイル配列は不変です — 新しい
fills/strokes配列を割り当ててください。fills[0]は変更しないでください。 - #2 トークンの適用は非同期です(約 100 ms) — 1 回の
execute_code呼び出しで適用し、後続の呼び出しでresolvedValueを確認してください。 - #3
resize()はテキストのgrowType: 'fixed'を強制します — 折り返したいテキストをリサイズした後に、growType = 'auto-width'/'auto-height'を設定してください。 - #4 Flex/grid は子要素の x/y を上書きします — 子要素は追加順と gap/padding/align/justify で配置してください。例外的に個別配置する場合のみ、
child.layoutChild.absolute = trueを設定してください。 - #5
width/heightとparentX/parentYは読み取り専用です —shape.resize(w, h)とpenpotUtils.setParentXY(shape, x, y)を使用してください。 - #6 インスタンスの内部を変更する前に切り離してください — 先に
detach()を実行し、その旨を報告してください。 - #7 追加しないとキャンバス上に表示されません —
container.appendChild(child)(またはpenpot.currentPage.root)を使用してください。 - #8 パディングトークン — パディングトークンは 2.17 以降でバインドされます。古いインスタンスでは、
createScreenWrapper.jsがミラーリングした数値と台帳の例外にフォールバックします。 - #9
switchVariant(pos, value)—posはvariants.properties(storage.run.ds.componentsByRole[role].axesを参照)におけるインデックスです。軸名ではありません。 - #13b フォントの完全一致 —
penpot.fonts.all.find(f => f.name === "…")を使用してください。findByNameは部分一致検索です。 - #15 追加後に
layoutChildを設定 — 子要素がレイアウトボード内に入ってからlayoutChild.*を設定してください。openPageは非同期です。 - #16 塗りつぶしサイズ設定後に flipX — 塗りつぶしサイズ設定を使う各セクションの後に、
clearFlipスイープを実行してください(shared/visual-effects.md)。 - #17 レイアウトのジオメトリ — ジオメトリを読み取る前に
await board.waitForLayoutUpdate()を実行してください(sleep-100ms の慣例に代わるものです)。 - 使い慣れていないシグネチャは、使用前に
penpot_api_info(type, member)で確認してください。
5. トークン対応ブリーフの契約
変更を加える前に、依頼内容をこの契約として言い換えてください。デザインシステムを唯一の信頼できる情報源として扱い、トークンが存在する場合は値をハードコードしないシニアデザインエンジニアとして行動してください。
- コンテキスト — このコードがどの製品/画面のものか、誰が使うのか、どのページ/ビューを表すのか(例:「B2B 管理コンソールの設定ページ、デスクトップ」)。
- 目的 — 単一かつ具体的に記述します。「既存の
semanticトークンセットにバインドし、Button、InputField、Cardコンポーネントを再利用して、<SettingsPage>コンポーネントを編集可能な Penpot ボードとして再構築する。」 - 入力 — 実際のコード(コンポーネントファイル、CSS/Tailwind 設定、あればデザイントークンのエクスポート)、対象の Penpot ページ、検出したトークンセット、検出したコンポーネントライブラリ。
- 制約 — 禁止事項(生の hex 値を使わない、4px グリッドから外れたスペーシングを使わない、コンポーネントを独自に作り直さない、承認なしに
detach()しない)、厳守事項(プリミティブではなくセマンティックトークンを使う、新しいシェイプより既存のコンポーネントを優先する)、ビューポート幅。 - 受け入れ基準 — 定量的な基準:色/スペーシング/角丸/文字組みの値の 95% 以上がトークンにバインドされている(
validateScreen.jsで検証);孤立した生の値としてフラグ付けされたものがゼロ;対応するライブラリコンポーネントがある繰り返し使用される UI 部品はすべてシェイプではなくインスタンスである;すべてのスペーシングが 4px グリッドに沿っている;レイヤー名が意味を表している(Rectangle 12のような名前を使わない)。
6. 必須ワークフロー
視覚的なセルフレビュー(必須): 視覚的な成果を示すすべての ✋ チェックポイントの前に、
shared/visual-self-review.mdのエクスポート → 確認 → 修正ループを実行してください。直前に作成した単位を エクスポートし、チェックリストに照らして画像を自分で確認し、目に見える不備を修正してください(最大 2 回)。残った不備があれば明記したうえで、同じエクスポートを提示してください。
フェーズ 0 — 調査(読み取り専用)
目標: 既存の Penpot デザインシステムを把握し、コードの値を適切に割り当てられるようにします。変更は加えません。
high_level_overviewを 1 回実行します。execute_code:scripts/inspectDesignSystem.jsを実行し、トークンセット、すべてのトークン(name、type、resolvedValue)、すべてのライブラリコンポーネント(name、id、バリアント軸)を列挙します。storage.run.dsにキャッシュします。execute_code:penpotUtils.tokenOverview()を実行し、アクティブなテーマ/セットを確認します。- 入力コードを セクションの概要(ヘッダー、ナビゲーション、ヒーロー、フォーム、カードグリッド、フッターなど)と スタイル一覧(コードで使われている色、間隔、角丸、フォントの値をすべて)に解析します。
終了条件: (a)解決済みの値をキーとしたトークンマップ、(b)役割をキーとしたコンポーネントカタログ、(c)セクションの概要、(d)各値に対応するトークンを割り当てるか
UNMAPPEDとしてフラグを付けたスタイル一覧、が揃っていること。 ✋ チェックポイント: トークン/コンポーネントのカバー状況とUNMAPPEDの一覧を提示します。どのUNMAPPEDの値を新しい提案トークンにするか、または記録済みの例外として許容するかを尋ねます。references/01-discovery.mdを参照してください。
フェーズ 1 — 画面ラッパー
目標: 適切なフレックスコンテナを備えた画面の Board を作成し、RUN_ID の台帳エントリを追加します。
execute_code:scripts/createScreenWrapper.jsを実行します(Board 名を基準に冪等)。ビューポートサイズの Board を作成し、addFlexLayout('column')を設定します。間隔とパディングを間隔トークンにバインドします(2.16.x では、パディングをミラーリングし、padding-mirrors-tokenの例外を設定)。 終了条件: 空で正しいサイズのフレックス列 Board が 1 つ存在し、その ID が台帳に記録されていること。 ✋ チェックポイント: 空の Board をexport_shapeし、要素を追加する前に寸法/方向を確認します。
フェーズ 2 — セクションの段階的な構築(セクションごとに繰り返す)
目標: コードの断片から、セクションを1 つだけシステム上に構築します。references/02-component-assembly.md、references/03-token-binding.md、references/04-incremental-transform.md を参照してください。
execute_code:対象のセクション 1 つに対してscripts/buildSection.jsを実行します。一致するカタログコンポーネントがある箇所ではcomponent.instance()を優先し、独自仕様の箇所に限って図形を直接作成します。applyTokenを使って、塗り、間隔、角丸、タイポグラフィの値をすべてトークンにバインドします。execute_code(別の呼び出し。エージェントのターンから約 100 ms 空けて実行):token.resolvedValue/shapeStructureを読み戻し、バインドが適用されたことを確認します。- セクションを
export_shapeします。 終了条件: セクションがレンダリングされ、すべての値がトークンにバインドされるか、承認済みの例外となっており、コンポーネントがインスタンスであること。 ✋ チェックポイント: セクションを提示します。「問題ありません」という承認の対象はこのセクションのみです。続行する前に、次のセクションを指定してください。
フェーズ 3 — 検証とレポート
目標: 画面がシステムに準拠していることを証明します。
execute_code:scripts/validateScreen.jsを実行し、トークンカバー率、孤立した生の値の一覧、構造の妥当性(フレックス設定、自動命名レイヤーの有無、インスタンスと図形)を確認します。- 構造化レポートを作成します(AGENTS.md のセクション 12)。 終了条件: 受け入れ基準を満たしているか、すべての不足事項がレビュー用に明示されていること。 ✋ チェックポイント: カバー状況と残りの不足事項を提示し、承認を求めます。
7. 重要なルール
- 常に最初に
high_level_overviewを実行します。書き込みを行う前に、必ず Phase 0 の検出を行います。 execute_codeによる変更呼び出しごとに、セクションは1つだけにします。1回の呼び出しで複数のセクションを作成してはいけません。- 色、間隔、角丸、境界線、文字組みの値はすべて、適用前にトークンに対応付ける必要があります。一致するトークンがある場合、図形に生の hex/rgb/px 値を指定してはいけません。
- 間隔、パディング、ギャップはすべて4pxの倍数にします(コード値を最も近いグリッドトークンに丸め、例外があれば記録します)。
- テーマ設定を機能させるため、プリミティブよりもセマンティック層のトークンを優先します(
tokens-schema.json)。 - 対応するライブラリコンポーネントがある UI パーツは、手作りの図形グループではなく、必ずインスタンスにします。
- トークンを新たに作ってはいけません。コード値に一致するものがない場合は、トークン案(名前/値/階層)を提示し、承認を待ちます。
- トークンの適用は非同期です。後続の
execute_code呼び出しでresolvedValueを確認し、同じ呼び出し内では確認しません。 - 明示的な承認なしにインスタンスを
detach()してはいけません。実行した場合は報告します。 - コード上の役割に基づいたセマンティックなケバブケースのレイヤー名(
nav、header、card-container)を使用し、Rectangle 12のような名前は使用しません。 - 各フェーズの後に
RUN_ID台帳を更新し、作成処理はすべて冪等にします(名前で存在確認を行います)。
8. ドメインアーキテクチャ
このスキルは画面ツリーを生成し、デザインシステムマップを使用します。
- デザインシステムマップ(Phase 0 の出力。
storage.run.dsにキャッシュ):tokensByValue: { "#0066FF": "color.action.primary.bg", "16": "spacing.4", ... }— コード値からトークンへの高速な検索に使う逆引きインデックス。tokensByName: { "color.action.primary.bg": {type, resolvedValue} }。componentsByRole: { button: {id, variants}, input: {...}, card: {...} }。
- セクション概要(コードから取得): 順序付きリスト
[{ role, codeFragment, childrenRoles }]。 - スタイル一覧:
[{ codeValue, cssProp, matchedToken | "UNMAPPED" }]。 - 画面ツリー(Penpot の出力):
Board(flex column) → section Boards(flex) → コンポーネントインスタンス + バインド済み図形。 - 台帳(
RUN_ID):{ runId, phase, created:[{kind,name,id}], proposedTokens:[...], exceptions:[...], pendingReview:[...] }。
9. モードとポリシー
デフォルトはレビュー(レビュー付き適用)です。各セクションを作成した後、チェックポイントで export_shape と要約を提示してから次に進みます。リンク:shared/modes-and-policies.md。
- 安全に適用できる項目(自動適用可): セマンティックなレイヤー名への変更、トークンの解決済み値と完全に一致する生の値をそのトークンに置き換えること(情報を損なわない置換)、プラグインデータ/メタデータの追加。
- 必ずレビューを要求する項目: 画面の Board とジオメトリの作成、コンポーネントのインスタンス化/再構成、新しいトークンの提案、
detach()、完全一致ではなく判断を要するトークンの照合。
10. 状態管理
リンク:shared/state-management.md。台帳の名前空間は penpot-ai、キーは RUN_ID です(例:bfc-2026-06-05-a)。
storage.run.ds— Phase 0 で取得したキャッシュ済みデザインシステムマップ(トークン、コンポーネント)。setSharedPluginData('penpot-ai', '<RUN_ID>.phase', n)— 現在のフェーズ。setSharedPluginData('penpot-ai', '<RUN_ID>.ledger', JSON)—{boardId, sectionsBuilt:[role], proposedTokens, exceptions}。- 再開: 台帳を再読み込みし、
inspectDesignSystem.jsを再実行して DS マップを再構築し、shapeStructure(board)で存在するセクションを確認して、ledger.phaseから続行します。冪等な手順により、安全に再実行できます。
11. ユーザー確認ポイント
| フェーズ後 | 表示する成果物 | 確認する内容 |
|---|---|---|
| 0 検出 | トークン/コンポーネントの網羅状況表、未対応のスタイル値 | 「どの未対応の値を新しいトークンとして提案し、どれを記録済みの例外としますか?」 |
| 1 ラッパー | 空の Board の export_shape と寸法 | 「ビューポートのサイズと列の向きは正しいですか?」 |
| 2 セクション(各セクション) | セクションの export_shape とトークンのバインド状況の要約 | 「このセクションを承認しますか?(次のセクションの名前を明示します)」 |
| 3 検証 | 網羅率(%)、孤立値の一覧、構造の健全性 | 「承認しますか?それとも先に、記載された不足に対処しますか?」 |
12. 命名規則
リンク:shared/naming-conventions.md。ドメイン固有の詳細:
- Screen Board:
screen-<view-name>(kebab ケース)、例:screen-settings。 - Section Boards:意味的な HTML ロール —
header、nav、main、section、footer、またはcard-grid、field-rowのような kebab ケースのロール名。 - コンポーネントのインスタンスには、カタログのコンポーネント名をそのまま使用します。
- 提案するトークンには、
tokens-schema.jsonのドット表記による階層を使用します。コードでの使用方法から導き出される意図を表す意味的な名前を優先してください(color.blue.500ではなくcolor.action.primary.bg)。
13. 非合理化への対処表
| LLM が持ち出す言い訳 | それが誤りである理由 | フローを停止させる決定論的な対策 |
|---|---|---|
「CSS に #0066FF とあるので、そのまま塗りに設定します。」 | 16 進数の直書きはシステムから外れており、テーマに対応できず、ガバナンスにも違反します。 | storage.run.ds.tokensByValue で検索します。一致した場合は → applyToken。一致しない場合は → トークンを提案し、承認のために停止します。生の 16 進数は決して設定しません。 |
「Tailwind の p-5 は 20px なので、20 に設定すれば十分です。」 | 数値の直書きはトークンのスケールを迂回し、グリッドから外れる可能性があります。 | 最も近い 4px グリッドのスペーシングトークンに丸めて applyToken を実行します。例外は承認を得た場合に限り記録します。 |
「Button コンポーネントがありますが、テキスト付きの角丸長方形を描くほうが速いです。」 | システムの要素を独自に作り直すと、更新、バリアント、引き継ぎ時の整合性が損なわれます。 | storage.run.ds.componentsByRole.button が存在する場合は、component.instance() を使ってインスタンス化し、プロパティ/バリアントを設定します。独自仕様の要素に限り、生のシェイプを使用します。 |
「呼び出し回数を減らすため、ページ全体を 1 回の execute_code で構築します。」 | 一括実行では失敗が見えにくくなり、バインドされていない未検証の出力が生まれます。 | 呼び出しごとに 1 つのセクションを構築し、後続の呼び出しでトークンのバインドを検証します。次に進む前に export_shape を実行してチェックポイントを作成します。 |
「このコード値にはトークンがないので、作業を続けるために color.misc.x を自分で追加します。」 | トークンを黙って作るとシステムが汚染され、人によるガバナンスを回避してしまいます。 | 名前/値/階層を指定して ledger.proposedTokens に追加し、発見チェックポイントで停止します。独断で作成してはいけません。 |
| 「トークンの読み戻しができなかったので、フォールバックとして値も直書きします。」 | 直書きのフォールバックは、このスキルが禁止する孤立した値そのものです。読み戻しは非同期で行われます。 | 後続の execute_code 呼び出しで resolvedValue を再読み込みします。本当に失敗していた場合はバインドを修正し、生のフォールバック値は決して追加しません。 |
14. ヘルパーコードのスニペット
// Reverse-index lookup: code value -> token (Phase 2, inside buildSection)
const ds = storage.run.ds;
const tokenName = ds.tokensByValue[String(codeValue).toLowerCase()]; // e.g. "#0066ff" -> "color.action.primary.bg"
if (tokenName) {
const token = penpotUtils.findTokenByName(tokenName);
shape.applyToken(token, ["fill"]); // verify resolvedValue in a LATER call
} else {
ledger.proposedTokens.push({ from: codeValue, suggest: "propose a semantic token", tier: "semantic" });
}
return { applied: !!tokenName, codeValue };
// Prefer an existing component over a raw shape
const role = "button";
const cat = storage.run.ds.componentsByRole[role];
let node;
if (cat) {
const comp = penpot.library.local.components.find(c => c.id === cat.id);
node = comp.instance(); // instance, not a redrawn box
} else {
node = penpot.createRectangle(); // bespoke only
}
container.appendChild(node);
return { role, usedComponent: !!cat, id: node.id };
15. 参考リソース
penpot_api_info("LibraryComponent")、penpot_api_info("Shape", "applyToken")、penpot_api_info("Board", "addFlexLayout")、penpot_api_info("Token", "resolvedValue")— 使用前に確認してください。shared/penpot-mcp-tool-reference.md(ツールのインターフェース)、shared/plugin-api-gotchas.md(注意点)、shared/tokens-schema.json(トークンの種類/階層)、shared/naming-conventions.md、shared/state-management.md。- 関連スキル:
penpot-build-screen(ブリーフに基づく生成フロー)、penpot-foundations(適用できるトークンがシステムにない場合。その推論モードを使用)、penpot-audit-accessibility(構築後のコントラスト確認)。
16. 補助ファイル
references/
| ファイル | 読み込み時期 | 目的 |
|---|---|---|
references/01-discovery.md | Phase 0 | トークン/コンポーネントを列挙し、値→トークンの逆引きインデックスと役割別コンポーネントカタログを作成します。コードを解析し、セクションのアウトラインとスタイル一覧を作成します。 |
references/02-component-assembly.md | Phase 2 | インスタンスにするかシェイプにするかを決定します。ライブラリのコンポーネントをインスタンス化し、バリアント/プロパティを設定します。フレックスで独自パーツを組み立てます。 |
references/03-token-binding.md | Phase 2 | コードのスタイルをセマンティックトークンにマッピングします。applyToken のプロパティ名、非同期検証、新しいトークンの提案を扱います。 |
references/04-incremental-transform.md | Phase 2 | セクションごとのループ、順序、台帳の更新、再開可能性を扱います。 |
references/05-anti-rationalization.md | 全フェーズ | コード変換で起こりやすい具体的な落とし穴を含む、拡張版の対策をまとめています。 |
references/06-error-recovery.md | エラー発生時 | バインドの失敗、孤立した値、フレックスと x-y の競合、デタッチの失敗を診断します。 |
scripts/
| ファイル | フェーズ | 目的 |
|---|---|---|
scripts/inspectDesignSystem.js | 0 | トークンセット/トークンとライブラリのコンポーネントを列挙し、値→トークンの逆引きインデックスと役割別コンポーネントカタログを作成して、storage にキャッシュします。 |
scripts/createScreenWrapper.js | 1 | フレックスの縦方向レイアウトと、トークンにバインドされたパディング/間隔を備えた画面 Board を冪等に作成します。 |
scripts/buildSection.js | 2 | コードの断片から1つのセクションを構築します。コンポーネントをインスタンス化し、独自のシェイプを作成して、トークンをバインドします。 |
scripts/validateScreen.js | 3 | トークンのカバレッジを計算し、孤立した生の値を一覧表示して、構造の整合性(フレックス、名前、インスタンス)を確認します。 |
ドクトリンのパス。 ネイティブインストールでは、shared/… と policies/… はこのバンドル内で解決されます(インストーラーによってベンダー化されます)。Claude Code プラグインのインストールでは、これらはプラグインのルートに配置されます — このファイルから2階層上の ${CLAUDE_PLUGIN_ROOT}/shared/… です。