cc-figma-component について
cc-figma-componentは、コンポーネント契約からFigmaのコンポーネントセットを生成するスキルです。契約に沿ってコンポーネント固有トークンを導出し、バリアントや構成を反映してFigma変数に紐付けます。
主な機能
- 01コンポーネント契約の読み込み
- 02コンポーネント固有トークンの導出
- 03Semantic変数へのプロパティ紐付け
- 04バリアント軸と状態に基づくセット作成
- 05契約で定義されたコンポーネント構成の適用
活用シーン
- 01契約からFigmaコンポーネントを生成
- 02プロパティと状態に基づくバリアントセット作成
- 03コンポーネントの見た目をSemantic変数で管理
MCP 対応
Figmaの`use_figma`呼び出しを使い、Figmaファイル内のページやコンポーネントの確認・作成、変数の紐付けを行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nvillapiano/ component-contracts-figma --skill cc-figma-componentインストール後、Claude Code で/cc-figma-componentを入力して読み込まれたか確認します。
詳細
cc-figma-component — コンポーネントコントラクト コンポーネントスキル
コンポーネントコントラクトから Figma のコンポーネントセットを生成します。このスキルはコントラクトを読み取り、コンポーネントスコープ(Tier 3)のトークンを導出し、Figma 内で完全に変数にバインドされたコンポーネントセットを構築します。
前提条件 — 先にこれらのスキルを読み込んでください:
figma-use— Plugin API の構文ルール(すべてのuse_figma呼び出しの前に必須)figma-generate-library— デザインシステムのワークフローとコンポーネント作成パターンcc-figma-tokensが実行済みであること — 対象の Figma ファイルにPrimitivesコレクションとSemanticコレクションが存在している必要があります
このスキルの一環として use_figma を呼び出す際は、常に skillNames: "cc-figma-component" を渡してください。
グローバルルール
フレームのサイズ設定はデフォルトで HUG
このスキルで作成されるすべてのフレームは、作成直後に両方のサイズプロパティを 'HUG' に設定する必要があります — 例外はありません。
node.layoutSizingHorizontal = 'HUG';
node.layoutSizingVertical = 'HUG';
これは次に適用されます: ベースコンポーネントフレーム、すべてのバリアントの複製、Generation Notes フレーム、および実行中に作成されるその他すべてのフレーム。Figma のデフォルトに依存しないでください — 新しいフレームは FIXED の 100px に設定され、パディングの値に関係なく縦長のコンポーネントが生成されます。HUG から逸脱するのは、スロットの子の figma.sizing がコントラクトで明示的に "fixed" に設定されている場合のみです。その場合は、コントラクトの figma.width と figma.height の値を直接使用します:
if (child.figma?.sizing === 'fixed') {
node.layoutSizingHorizontal = 'FIXED';
node.layoutSizingVertical = 'FIXED';
node.resize(child.figma.width, child.figma.height);
}
これが HUG デフォルトに対する唯一の正当な例外です。
1. 設定
何よりも先に、プロジェクトルートから .component-contracts を読み取ってください。どのような応答においても FIGMA_ACCESS_TOKEN を出力しないでください。
FIGMA_ACCESS_TOKEN=... # never output this value
FIGMA_FILE_KEY=... # use for all use_figma calls
TOKENS_DIR=... # root directory of token files
CONTRACTS_DIR=... # root directory of contract files
2. コントラクトの構造
コンポーネントコントラクトは {CONTRACTS_DIR}/{id}.contract.json にある JSON ファイルです。何かを計画する前に、完全に読み取ってください。
このスキルが使用する主要なセクション:
tokens — コンポーネントの Tier 3 トークンバインディングをカテゴリ別に定義します:
{
"tokens": {
"color": {
"background.primary": "brand.500",
"text.primary": "neutral.0",
"border.secondary": "brand.500"
},
"spacing": {
"padding.x.sm": "space.sm",
"padding.y.sm": "space.xs"
},
"typography": {
"label.size": "textSize.sm",
"label.weight": "weight.semibold"
},
"border": {
"radius": "radius.md",
"width": "border.width.sm"
},
"motion": {
"transition": "duration.fast"
}
}
}
props — コンポーネントのバリアント軸(例: variant、size、state)を定義します
a11y — アクセシビリティ要件(ARIA ロール、キーボードの動作)
platforms — 対象プラットフォーム(web、ios、android)
3. Tier 3 トークンの導出
コンポーネントスコープ(Tier 3)のトークンは、コントラクトの tokens セクションから導出されます — 別のファイルとして保存されるものではありません。以下のルールを使用して実行時に導出してください:
命名規則
--ds-{componentId}-{role}-{qualifier}
ここで:
componentId— コントラクトのidフィールド(例:button、accordion)role— Figma に合わせたロール名(以下のロール変換表を参照)qualifier— コントラクトの値にある Semantic エイリアス名から導出されます
ロール変換表
| コントラクトのカテゴリ + キープレフィックス | CSS/Figma ロール |
|---|---|
color.background.* | fill |
color.text.* | text |
color.border.* | border |
color.focus.* | focus |
spacing.* | spacing |
typography.* | typography |
border.radius | border(border-radius として) |
border.width | border(border-width として) |
motion.* | transition |
修飾子の導出
修飾子はコントラクトの Semantic エイリアス値から取得し、スケールのサフィックスを除いたものです:
"background.primary": "brand.500" → qualifier = "brand" → --ds-button-fill-brand
"background.primary.hover": "brand.600" → qualifier = "brand-hover"
"text.primary": "neutral.0" → qualifier = "neutral-0"
状態のサフィックス(hover、active、focus、disabled)は修飾子の後に付加されます:
--ds-button-fill-brand-hover
--ds-button-fill-brand-active
Semantic 変数の検索
各 Tier 3 トークンは、Figma 内の対応する Semantic 変数にバインドする必要があります。変数を名前で検索してください:
contract value "brand.500" → Semantic variable named "brand/500"
contract value "space.sm" → Semantic variable named "space/sm"
contract value "radius.md" → Semantic variable named "radius/md"
Semantic 変数が見つからない場合は、処理を続ける前に停止し、不足している変数をユーザーに報告してください。
4. コンポーネントセットのアーキテクチャ
プライマリバリアントプロパティ(例: variant)の各値は、単一の統合セットではなく、別々のコンポーネントセットになります。セカンダリ軸(Size、State)は各セット内のバリアントプロパティになります。
命名規則
{ComponentName}/{VariantValue} → Button/Primary, Accordion/Multiple
- コンポーネント名: タイトルケース(
Button、Accordion) - バリアント値: タイトルケース(
Primary、Secondary) - 区切り文字:
/
各コンポーネントセット内のバリアント軸
プライマリバリアント以外のすべての軸は、コンポーネントセット内のプロパティになります。
Button/Primary:
Size=Small, State=Default
Size=Small, State=Hover
Size=Small, State=Focus
Size=Small, State=Active
Size=Small, State=Disabled
Size=Small, State=Loading
Size=Medium, State=Default
... (18 total variants per set)
プロパティの命名
- プロパティ名: タイトルケース、コントラクトのプロパティ名と一致させる(
Size、State) - プロパティ値: タイトルケース(
Small、Medium、Large、Default、Hover、Focus、Active、Disabled、Loading)
標準の状態値
状態は必ずコントラクトの states 配列から読み取ってください — ハードコードされたリストは使用しないでください。Figma のプロパティ値では、各値をタイトルケースにします(例: "default" → "Default")。コンポーネントごとに状態のセットは異なります — Button は Loading を含む 6 つ、Accordion は Loading のない 5 つです。
5. 実行モード
デフォルト(高速): すべてのフェーズを停止せずに順番に実行します。Phase 0 の計画を提示して承認を待ち、その後 Phase 1〜7 を中断なく実行します。これがすべての実行におけるデフォルトです。
デバッグモード: 各フェーズの後に停止し、続行する前に明示的な承認を待ちます。問題を診断する場合や中間出力を検証する場合に使用します。プロンプトに「debug mode」を含めることで有効になります。スクリプトはデバッグモードでのみ保持されます — 完了したら手動でクリーンアップしてください。
スクリプトの場所: すべての作業ファイルは
scripts/サブディレクトリに書き込まなければなりません — リポジトリのルートには決して書き込まないでください。ルートレベルのドットファイル(.mcp-args-phase3.json、.phase2-code-raw.jsなど)は rimraf ステップではクリーンアップされず、リポジトリに蓄積されます。
Note:
scripts/を.gitignoreに追加してください — これらは生成された作業ファイルであり、ソース成果物ではありません。
6. ワークフロー
Phase 0 — ディスカバリー(読み取り専用)
.component-contracts設定を読み取るCONTRACTS_DIRからコントラクトファイルを読み取る- Figma ファイルを検査する — 既存の Semantic/Primitive コレクションと、この名前の既存コンポーネントがあるか確認する
- Semantic コレクションと Primitive コレクションが存在することを確認する — 存在しない場合は停止し、最初に
cc-figma-tokensを実行するようユーザーに伝える - コントラクトの
tokensセクションから完全な Tier 3 トークンリストを導出する - すべての Semantic 変数を検索する — 続行する前にすべてが存在することを確認する
- コントラクトの
propsセクションから完全なバリアントマトリクスを構築する - 完全な計画をユーザーに提示する:
- コンポーネント名とバリアント数
- Semantic エイリアスを含む完全な Tier 3 トークンリスト
- 不足している Semantic 変数
- デバッグモードの場合: 続行する前にユーザーの明示的な承認を待つ
Phase 1 — ページのセットアップ
- コンポーネント専用のページを作成する:
{ComponentName}(例:Button、Accordion) await figma.setCurrentPageAsync(page)でそのページに切り替える- ページ ID を返す
フェーズ 2 — ベースコンポーネント
まず 1 つのベースコンポーネント(プライマリバリアントの Default 状態)を構築します:
- 自動レイアウト、水平方向の Frame を作成します
- 作成直後に、次の正確な 2 行を使ってサイズを HUG に設定します — どちらも必須で、片方だけでは不十分です:
Figma は新しいフレームをデフォルトでframe.layoutSizingHorizontal = 'HUG'; frame.layoutSizingVertical = 'HUG';FIXEDの 100px にします。垂直方向の行を省略すると、パディング値に関係なく縦長のボタンが生成されます。子ノードやバインディングを追加する前にこれを行ってください。 - すべての構造プロパティ(パディング、ギャップ、半径)を Semantic 変数にバインドして適用します
- コントラクトの
composition.slotsセクションを読んで子ノードを追加します。childOrderを使ってレンダリング順序を決定します — 固定の順序を仮定しないでください:composition.slots.{slotName}.childOrderを読み取ります — この配列はフレームに子が追加される順序を定義しますchildOrder内の各子について、そのfigmaヒントを読み取ります:figma.textProp— テキストノードを作成し、そのコンポーネントプロパティ名に接続しますfigma.booleanProp— アイコンプレースホルダーフレームを作成し、visible: falseを設定し、その BOOLEAN プロパティ名に接続しますfigma.position: "trailing"— この子は他の子に関係なく最後に配置されますfigma.rotateOnExpand— 回転は変数バインドできないことを Generation Notes に記載しますfigma.visibleWhen— 可視性は状態駆動であることを Generation Notes に記載します
composition.slotsが定義されていない場合は、次のフォールバックを使用します:アイコンプレースホルダー(非表示)、次にラベルテキストノード- アイコンプレースホルダーフレームは常に固定サイズ(md の場合は 20×20)、塗りなし、デフォルトで
visible: falseです
- すべての視覚プロパティを Semantic 変数にバインドします — ハードコードされた値は禁止です
- すべての子を追加した直後に、作成後のサイズチェックを実行します。 これは、minifier が HUG 割り当てを落とした可能性のあるフレームを修正します:
// Walk every frame in the component subtree and enforce HUG function enforceHug(node) { if (node.type === 'FRAME' || node.type === 'COMPONENT') { if (node.layoutSizingVertical === 'FIXED') { node.layoutSizingHorizontal = 'HUG'; node.layoutSizingVertical = 'HUG'; } } if ('children' in node) node.children.forEach(enforceHug); } enforceHug(baseComponent); - すべてのノードに再帰的に
setExplicitVariableModeForCollectionを適用します - 検証:
get_screenshot— 続行する前に、ボタンが高さより幅が広いことを確認します - デバッグモードの場合:ユーザーのチェックポイントを待機します
フェーズ 3 — バリアントマトリクス
プライマリバリアントプロパティの各値について、個別のコンポーネントセットを作成します:
Size × Stateの組み合わせごとにベースコンポーネントを複製します- すべての複製で、トークンオーバーライドを適用する前に、次の正確な 2 行を使ってサイズを HUG にリセットします:
複製は元のサイズモードを保持する場合がありますが、例外なくすべてのコピーでこれを明示的に再設定する必要があります。duplicate.layoutSizingHorizontal = 'HUG'; duplicate.layoutSizingVertical = 'HUG'; - 状態ごとにバリアント固有のトークンオーバーライドを適用します(例:hover は塗り色を変更します)
- 各コンポーネントに名前を付けます:
Size=Small, State=Defaultなど(タイトルケース、Property=Value) combineAsVariantsを呼び出す前に、すべてのバリアントフレームに対して HUG 強制パスを実行します。これはセーフティネットです — エージェントが見落としたフレームを捕捉します:
このパスの後でも高さが 100px のままのバリアントは、サイズの問題ではなくバインディングの問題を示しています。variants.forEach(v => { v.layoutSizingHorizontal = 'HUG'; v.layoutSizingVertical = 'HUG'; });combineAsVariantsを呼び出してコンポーネントセットを作成します- コンポーネントセットの名前を
{ComponentName}/{VariantValue}に変更します(例:Button/Primary) combineAsVariantsの直後に:バリアントを手動でグリッドレイアウトし、実際の境界に合わせてサイズ変更します:const COLS = 6; // one column per state const GAP = 40; const PADDING = 40; // padding inside component set on all sides variants.forEach((v, i) => { v.x = PADDING + (i % COLS) * (v.width + GAP); v.y = PADDING + Math.floor(i / COLS) * (v.height + GAP); }); // Resize to actual content bounds — never guess, always calculate const maxRight = Math.max(...variants.map(v => v.x + v.width)); const maxBottom = Math.max(...variants.map(v => v.y + v.height)); componentSet.resize(maxRight + PADDING, maxBottom + PADDING);- 各バリアント値(Primary、Secondary、Ghost、Destructive)について繰り返します
- 検証:
get_screenshot— すべてのサイズでボタンが高さより幅が広いことを確認します - デバッグモードの場合:ユーザーのチェックポイントを待機します
フェーズ 4 — 変数バインディングパス
すべてのバリアントノードについて:
- 塗りプロパティを正しい Semantic カラー変数にバインドします
- 線プロパティを正しい Semantic カラー変数にバインドします
- パディング/ギャップを正しい Semantic スペーシング変数にバインドします
- 角の半径を正しい Semantic 半径変数にバインドします
- バインド後に、すべてのノードとそのすべての子に再帰的に
setExplicitVariableModeForCollectionを呼び出します - 検証:
get_screenshot— 色が正しく解決されることを確認します(ゴースト化/未解決の変数がないこと)
フェーズ 5 — コンポーネントプロパティ
- コントラクトの
propsセクションに一致するコンポーネントプロパティを定義します:- TEXT
label— Loading を含むすべてのバリアントで、Label テキストノードのcharactersにリンクします。「Saving」のようなハードコードされた文字列で絶対に代用しないでください。ラベルテキストは常にlabelプロパティの値によって駆動されます。 - BOOLEAN
iconStart—componentPropertyReferencesを介して Icon プレースホルダーのvisibleプロパティにリンクします。デフォルトはfalse(非表示)です。これがアイコンスロットの表示・非表示を切り替える正しい方法です。 - BOOLEAN
iconOnly— コンポーネントセット上で定義します。レイアウトには自動的にリンクされません(既知の制約を参照)。 - BOOLEAN
fullWidth— コンポーネントセット上で定義します。レイアウトには自動的にリンクされません(既知の制約を参照)。
- TEXT
disabledやloadingを BOOLEAN プロパティとして追加しないでください — これらはバリアント状態としてのみ扱われます- ファイル内に実在するアイコンコンポーネントが存在しない限り、アイコン用の
INSTANCE_SWAPを追加しないでください - 検証: プロパティパネルがコントラクトの props と一致することを確認します
フェーズ 6 — キャンバスの注釈
最終検証の前に、コンポーネントページ上に ⚠️ Generation Notes フレームを作成し、エージェントが想定したこと、フラグを立てたこと、解決できなかったことをすべて記録します。これにより、ファイルを開く誰にとっても出力が自己文書化されます。
このフレームは、すべてのコンポーネントセットの下に、明確に分離して配置してください。薄い黄色の塗り(#FFFBEB)、自動レイアウトの垂直方向を使用し、サイズを明示的に設定します:
notesFrame.layoutSizingHorizontal = 'HUG';
notesFrame.layoutSizingVertical = 'HUG';
これにより、フレームは固定高さでクリップされるのではなく、すべての注記テキストを含むように拡張されます。タイトルのテキストノードを追加し、その後に個々の注記エントリを続けます。
該当する場合は、以下のそれぞれについて必ず注記を含めてください:
| 条件 | 記述する注記 |
|---|---|
focus/default がバリアントの塗りと同じ色 | 「フォーカスリングの色の競合: focus/default は [variant] の塗りの上で見えない可能性があります。focus/onBrand セマンティックトークンを定義して再実行してください。」 |
| Loading 状態にスピナーコンポーネントがない | 「State=Loading: スピナーコンポーネントが見つかりません。ラベルテキストは保持されています。スピナーのインスタンスを追加して iconStart に接続するか、専用のローディングインジケーターを作成してください。」 |
| Semantic ではなく Primitives にバインドされているトークンがある | 「トークンギャップ: [token name] にセマンティックエイリアスがありません。Primitives に直接バインドされています。トークンアーキテクチャに合わせるためにセマンティックエイリアスを追加してください。」 |
fullWidth または iconOnly がレイアウトに接続されていない | 「fullWidth / iconOnly はコンポーネントプロパティとして定義されていますが、レイアウトを自動的に駆動しません。既知の制約を参照してください。」 |
コントラクトに iconEnd プロパティが存在する | 「iconEnd は Figma でモデル化されていません — 末尾のアイコンスロットは作成されていません。2 つ目のアイコンプレースホルダーフレームを追加し、必要に応じて iconEnd BOOLEAN に接続してください。」 |
| コントラクトのプロパティが Figma でモデル化されていない | 「コントラクトの [prop name] が Figma でモデル化されていません: [reason]。」 |
| 生成中に行われた想定 | 明示的に文書化してください。 |
注記がない場合でも、フレームを作成し、「No issues — all tokens resolved, all props wired.」というテキストを記載してください。
フェーズ 7 — 最終検証
get_metadata— 構造を確認します: バリアント数、プロパティ定義、階層get_screenshot— 視覚的チェック: ボタンはすべてのサイズで幅が高さより大きい、テキストがクリップされていない、要素が重なっていない、間隔が正しい、バリアントが切り取られていない- 構造化されたサマリーを返します:
{
"component": "Button",
"componentSets": [
{ "name": "Button/Primary", "variantCount": 18, "id": "..." },
{ "name": "Button/Secondary", "variantCount": 18, "id": "..." },
{ "name": "Button/Ghost", "variantCount": 18, "id": "..." },
{ "name": "Button/Destructive", "variantCount": 18, "id": "..." }
],
"propertiesDefined": ["Size", "State", "label", "iconStart", "iconOnly", "fullWidth"],
"annotations": ["list of notes written to canvas"],
"tier3Tokens": [...],
"pageId": "..."
}
-
クリーンアップ — 成功・失敗にかかわらず、毎回の実行の最後に必ずこれを実行してください。唯一の例外はデバッグモードです。
スクリプトは使い捨てです。 保持、再利用、再生成を提案しないでください。ユーザーに terser の実行や invoke ファイルの書き換えを指示しないでください。毎回の実行で、まっさらな状態から新しいセットが生成されます。これらはソース成果物ではなく、ビルドキャッシュとして扱ってください。
scripts/ディレクトリと、リポジトリルートに書き込まれた作業ファイルを削除します:npx rimraf scripts/ rm -f .mcp-*.json .mcp-*.txt .*-phase*.js .*-phase*.json .*-args*.json .*-code*.js .*-code*.json .tmp-*.jsデバッグモードでは、検査のためにすべてのファイルをそのまま残してください。
7. 変数バインディングのルール
- コンポーネントはセマンティック変数にのみバインドします — プリミティブに直接バインドしてはいけません
- 対応するトークンを持つすべての視覚プロパティは変数にバインド MUST — ハードコードされた値は使用しないでください
- 変数バインディング操作の後は、すべてのノード AND すべての子ノードに対して再帰的に
setExplicitVariableModeForCollectionを呼び出す MUST - テキストノードに対する
setBoundVariableは、先にフォントを読み込む必要があります:await figma.loadFontAsync({family, style}) cornerRadiusはsetBoundVariableを直接サポートしていません — 個別の角を使用してください:topLeftRadius、topRightRadius、bottomLeftRadius、bottomRightRadius- 常に
setSharedPluginData/getSharedPluginDataを使用してください —setPluginData/getPluginDataは決して使用しないでください。非共有バージョンは、ツール実行ごとに変化するデバッグ UUID をプラグイン識別子として使用するため、保存されたデータが永久に取得できなくなり、ファイルが肥大化します。常にcomponent_contracts名前空間を使用してください:// CORRECT node.setSharedPluginData('component_contracts', 'dsb_key', 'component/button/base'); node.getSharedPluginData('component_contracts', 'dsb_key'); // WRONG — UUID changes per execution, data becomes orphaned node.setPluginData('dsb_key', 'component/button/base');
8. 既知の制約
-
フレームのサイズ設定はデフォルトで FIXED、100px — Figma では新しいフレームは高さ 100px の
FIXEDサイズ設定になります。すべてのフレーム、すべてのバリアントの複製、そして Generation Notes フレームで、両方の行を明示的に設定する必要があります:node.layoutSizingHorizontal = 'HUG'; node.layoutSizingVertical = 'HUG';水平方向のみを設定し、垂直方向を省略することが、縦長アスペクトのボタンができる最も一般的な原因です。省略記法はありません — 常に両方の行が必要です。
-
レタースペーシング — 変数にバインドすることはできません(em 単位は Figma の FLOAT と互換性がありません)。トークンの生の値から導出した固定のピクセル値として設定してください。
-
combineAsVariants は自動レイアウトを行いません — バリアントは (0,0) に積み重なります。常に直後に手動でグリッドレイアウトを行ってください。常に、すべてのバリアントの位置 + サイズ + パディングに対して
Math.maxを使って、コンポーネントセットを実際のコンテンツの境界にリサイズしてください。リサイズの寸法を推測してはいけません。 -
layoutSizingHorizontal/Vertical = 'FILL'はparent.appendChild(child)の後に設定する必要があります — 前に設定すると例外が発生します。 -
ゴーストモード — すべてのバインディング操作の後に、ノードとそのすべての子に対して再帰的に
setExplicitVariableModeForCollectionを常に呼び出してください。これを省略すると、変数が未解決として表示されます。 -
バリアント数の上限 — コンポーネントセットあたり Size × State > 30 バリアントになる場合は、作成する前にユーザーに確認してください。サブコンポーネントへの分割を検討してください。
-
フォーカスリングの色の競合 — スキルはフォーカスリングの色について判断を下しません。コントラクトの
color.focus.*トークンを読み取り、そこで定義されている Semantic 変数をすべてのバリアントに使用してください。focus/defaultがバリアントの塗りと同じ色である場合は、Generation Notes フレームでこれを指摘し、ユーザーにfocus/onBrandSemantic トークンを定義することを提案してください。スキル独自の判断でneutral/0やその他の色に置き換えないでください。 -
fullWidthとiconOnlyはレイアウトを自動的に制御できません — Figma Plugin API のcomponentPropertyReferencesはvisible、characters、mainComponentのみをサポートしています。boolean をlayoutSizingHorizontalにリンクしたり、アイコンのみとフルのレイアウトを切り替えたりすることはサポートしていません。これらのプロパティは spec/Code Connect の目的で定義されています。この制限を利用者向けに文書化してください — レイアウトの変更には手動でのバリアント作成か、後続のプラグインが必要です。 -
Figma プラグインサンドボックス —
atobは undefined — プラグインのランタイムにはネイティブの base64 デコードがありません。プラグインスクリプトでatobやbtoaを使用しないでください。スクリプトが 20k 文字の制限を超えて分割する必要がある場合は、base64 のポリフィルを使用するか、ロジックを複数の連続したuse_figma呼び出しに分割してください。 -
回転値は常に度数であり、ラジアンではありません — Figma Plugin API の
node.rotationプロパティは度数を受け付けます。常にコントラクトから度の値をそのまま渡してください(例:-90)。絶対にラジアン(-Math.PI/2)に変換しないでください。ラジアンを渡すと、Figma の UI では-1.57°のような値が表示され、視覚的な結果は正しくても見た目がおかしくなります。コントラクトのexpandedRotationとdefaultRotationフィールドは常に度数です。 -
Figma プラグインサンドボックス —
evalはブロックされています — 動的なコード実行が必要な場合は、回避策としてnew Function(code)()を使用してください。これが Figma 環境で分割されたプラグインスクリプトを組み立てて実行するための唯一の信頼できるパターンです。 -
アイコンスロットの可視性 — アイコンのプレースホルダーフレームはデフォルトで非表示(
visible: false)にし、componentPropertyReferencesを介してタイプvisibleの BOOLEAN コンポーネントプロパティ(iconStart)に紐付ける必要があります。アイコンスロットを常に可視のままにしないでください — アイコンを使用しないすべてのバリアントで空のボックスとして表示されます。