canvas-component-composability について
Drupal Canvas コンポーネントの合成性を設計するスキルです。Props、スロット、バリアント、コンポーネント境界を整理し、再利用可能なレイアウトや繰り返しコンテンツの構成を支援します。
主な機能
- 01Props とスロットの設計
- 02バリアントによるプリセット表現
- 03親スロットと子コンポーネントの活用
- 04コンポーネント境界の評価
- 05Canvas の Props モデリング制約への準拠
活用シーン
- 01再利用可能なカードグリッドの設計
- 02エディター向けコンポーネント操作項目の計画
- 03Props が多すぎるコンポーネントの再構成
- 04繰り返しコンテンツの子コンポーネント化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ drupal-canvas/ skills --skill canvas-component-composabilityインストール後、Claude Code で/canvas-component-composabilityを入力して読み込まれたか確認します。
同梱ファイルreferences/repeatable-content.md
詳細
多くの props を持つモノリシックなコンポーネントよりも、小さく、役割を絞ったコンポーネントを優先してください。コンポーネントに互いに無関係な props が増えてきたら、より小さく組み合わせ可能な部品に分解してください。
繰り返し表示するカード/リスト/グリッド UI では、Canvas コンポーネントを 2 つ使う構成を基本とします。繰り返し表示する子要素を受け取るスロットを持つ親レイアウトコンポーネントと、1 項目分の子コンポーネントです。繰り返し表示する項目を、car1Name、car2Name、feature1Title、card3Image のような番号付きの props グループにフラット化しないでください。
所有範囲
このスキルでは、次の再利用可能なモデリングルールを扱います。
- props とスロットの使い分け
- バリアントと粒度の細かい props の使い分け
- 繰り返しコンテンツのパターン
- 粒度の確認(分割、統合、スロットの抽出)
再利用を優先する基本方針については、
canvas-component-definition に従ってください。component.yml の正確な文法とスロットのスキーマについては、
canvas-component-metadata に従ってください。
既存コンポーネントの再利用
タスクで既存のコンポーネントが指定されている場合、またはコンポジションが明らかな場合は、新しいコンポーネントを作成する前に
canvas-component-definition の再利用チェックから始めてください。このスキルでは、その境界が明確になった後のコンポーネントの組み合わせ方に焦点を当てます。
参照マップ
必要な場合にのみ、参照を読み込んでください。
- 繰り返し表示するリスト/グリッドと、配列からスロットへの変換:
references/repeatable-content.md - 空の flex/grid コンテナにおけるスロットの操作性:
references/repeatable-content.md(「Slot container minimum size」セクション)
props とスロット
コンポーザビリティの主な仕組みにはスロットを使います。複雑なデータを props で渡すのではなく、親コンポーネントが子コンポーネントを受け入れられるよう、スロットを使ってください。これは、Canvas のユーザーがコンポーネントを別のコンポーネントの中に配置してページを構築する方法に合致します。component.yml を記述する前に、この設計時の基準を適用してください。
スロットと props の使い分け
| スロットを使う対象 | props を使う対象 |
|---|---|
| 数が変動する子コンポーネント | 単一の必須値(テキスト、URL) |
| ユーザーが組み合わせるコンテンツ | 設定オプション(サイズ、色) |
| 複雑な入れ子構造 | 単純なデータ(文字列、ブール値) |
| インスタンスごとに異なるコンテンツ | インスタンス間で一貫するコンテンツ |
画像 1 枚はスロットや複数の URL/alt props ではなく、1 つの prop として扱ってください。コンポーネントで画像を 1 枚使う場合は、Canvas の画像スキーマ ref を指定した image や backgroundImage などのセマンティックな画像 prop を使ってください。
繰り返し表示するカード/項目では、通常、親が見出し、導入文、配置、列数などのレイアウト props を持ち、各子コンポーネントがタイトル、画像、価格、ラベル、CTA、メタデータなどの項目コンテンツ props を持ちます。
リッチな内容を持つ繰り返し項目には、オブジェクトの配列を props で渡すのではなく、親のスロットと子コンポーネントを組み合わせるパターンを使ってください。references/repeatable-content.md を参照してください。
この基準をブロックに適用する際は、プリセットと単純なコピーは props に含めます。カードのグリッド、タブパネル、フッターの列、差し替え可能な領域は、スロット、または独自のスロットを持つ入れ子の子コンポーネントに含めます。大きなコンポジション領域を文字列 props や JSON の塊として表現しないでください。
バリアントと詳細なプロパティ
各コンポーネントでは、主となるモデリングスタイルを1つ使用します。場合によっては、バリアント1つと、直交する切り替え項目を1つか2つ組み合わせるハイブリッドも有効です。
バリアントをデフォルトとします。
- 主となる
variant列挙型で名前付きプリセットを選択します。各値は、スタイルと構造のルール一式を示します。 - デザインに名前付きプリセットがある場合や、任意の組み合わせの多くが無効になる場合は、主となる
variant列挙型を使用します。 - メインのプリセットフィールドのプロパティIDには、
appearanceやlayoutVariantなどの類義語ではなく、variantを使用します。 sizeやdensityなどの直交する列挙型は、それぞれの軸が独立して意味を持つ場合にのみ追加します。- バリアントを使うと、無効な組み合わせが減り、Canvasフォームがシンプルになり、デザインシステムやFigmaのバリアントとの整合性も高めやすくなります。
- 列挙値をCVAまたは同等の実装分岐やテーマトークンに対応付けます。
詳細なプロパティが適切なのは、次の場合です。
- ユーザーが各軸を個別に制御したいと明示している
- 多くの組み合わせで各軸が独立して有効である
- プロダクトのルールや分析に個別の切り替え項目が必要である
ハイブリッドモデリングも許容されますが、デザインが実際には少数の名前付きプリセットで表現される場合に、独立した設定項目を大量に並べるのは避けてください。
選択
| 状況 | 優先するもの |
|---|---|
| 相互排他的なレイアウト(A または B。自由な組み合わせは不可) | バリアント |
| デザインに名前付きプリセットやFigmaのバリアントがある | バリアント |
| 無効または未テストとなる組み合わせが多い | バリアント |
| プロダクトで直交する切り替え項目の自由な組み合わせが必要 | 詳細なプロパティ(またはハイブリッド) |
| ユーザーが各軸の個別の制御を求めている | 詳細なプロパティ |
分解の引き継ぎ資料に選択内容を記載し、実装とcomponent.ymlの整合性を保ってください。
引き継ぎ資料とメタデータでのプロパティの順序
Canvasエディターで使いやすいフォームの流れになるよう、分解の引き継ぎ資料とcomponent.ymlの順序を一致させてください。
- 主となるプリセット列挙型が1つある場合は、名前を
variantにして、最初に記載します。 - 次にコンテンツプロパティ(コピー、リンク、メディア)を記載します。
- 最後に、残りの設定プロパティ(二次的な切り替え項目、スタイル補助項目)を記載します。
主となるvariantがない場合は、コンテンツプロパティを先に、設定プロパティを後に記載します。
単一のvariantがなくても、モデルがそれ以外の点でプリセット中心の場合は、主要なプリセット列挙型があれば、引き続き最初に配置します。
Canvas固有の制約
- 繰り返し使用するリッチアイテムに、オブジェクト配列のプロパティは使用しないでください。代わりに、親スロットと子コンポーネントを使用します。
- プロパティはエディター向けです。実装専用の値を公開しないでください。
- 必須プロパティを、実装側で暗黙に設定されるデフォルト値に依存させないでください。メタデータとエディターで、そうした値を明示的に指定する必要があります。
- プロパティIDはラベルに合わせてキャメルケースにし、
canvas-component-metadataで正確な形を確定してください。
スロットを装うプロパティの場合
- 文字列内のJSONや、プロパティ内にシリアライズされたブロック
- 子UIのオブジェクト配列
これらは、スロットと子コンポーネントを組み合わせる構成に置き換えてください。
プロパティを装うスロットの場合
- 常に固定のプロパティを持つ1種類のコンポーネントだけを格納するスロット
その場合、親にそのマークアップを持たせるべきか、代わりに用途を絞ったプロパティを使用するべきかを検討してください。
component.ymlでのスロットの宣言
スロットはcomponent.ymlで宣言し、フレームワークのスロット機構を通じて使用してください(Reactでは名前付きプロパティ、VueとAstroでは名前付きスロット)。
正確なスロットスキーマと制約(mapと[]の使い分け、スロットキー、childrenの扱い)については、信頼できる情報源としてcanvas-component-metadataに従ってください。
粒度の確認
コンポーネントの境界が粗すぎるか細かすぎるかを監査する際は、次のヒューリスティクスを使用してください。これらは、最初のツリーとプロパティ/スロットの概要を作成した後に適用します。エディターで各コンポーネントの内容を把握しやすくし、各インスタンスでプロパティをコピー&ペーストせずに再利用できるようにすることが目的です。
コンポーネントを分割すべきサイン
次のような場合は、コンポーネントの分割を検討してください。
- 独立した選択肢ではなく、少数の名前付きプリセットを表す細かな切り替え項目が多数ある 場合。ユーザーが細かな制御を求めていない限り、バリアントを優先してください。
- 1 つの
machineNameが無関係な役割を担っている - 異なる目的を持つプロパティが 6~8 個を超えている
- 単独のコンポーネントとして意味をなす要素(パンくずリスト、 タイトル、メタデータ、ナビゲーション)のプロパティがある
- プロパティが増え続けており、それぞれのプロパティが視覚的に独立したブロックに対応している
- コンポーネントを使える場所を制限する、組み込みのレイアウト前提がある
- 独立して再利用できる、視覚的に異なるセクションが複数ある
- インデックスや接頭辞/接尾辞だけが異なるプロパティのグループが繰り返されている
(
item1Title、item2Title、item3Title) - レイアウトに加えて、表示順を変更できる独立したコンテンツ列が複数ある
- レイアウトの外枠と、独立した複数のコンテンツブロックを親コンポーネントが混在させている
- 子要素を作成者が直接配置できるようにする代わりに、どの子要素が存在するかを
showX/showYの連鎖で表している
一般的な分割パターン
ページレベルの要素は別々のコンポーネントにする
多くのページに表示されるものの、常に一緒に必要とは限らない要素は、 別々のコンポーネントにしてください。
繰り返しパターンを小さなコンポーネントに抽出する
同じ要素の組み合わせが繰り返される場合は、抽出してください。
- 日付 + カテゴリー/タグ ->
article-meta - カバー画像 + ダウンロードボタン ->
resource-cover - ラベル + 値の組み合わせ ->
metadata-item - アイコン + テキストリンク ->
icon-link - グリッド/リストのラッパー + 繰り返し表示するカード ->
card-grid+card - 注目の車のラッパー + 繰り返し表示する車カード ->
featured-cars-grid+car-card
組み込みレイアウトではなく、レイアウトコンポーネントを使う
コンテンツコンポーネントに 2 列やグリッドのレイアウトを組み込まないでください。 レイアウトコンポーネントを使い、その中にコンテンツを組み合わせてください。
子コンポーネントを抽出する場合
次のいずれかに当てはまる場合は、子コンポーネントを抽出するか、スロットを追加してください。
- 同じ役割のブロックが 2 回以上表示される
- 明確な名前があり、サイト内の別の場所にも表示される可能性がある
- 作成者がそのブロックを独立して並べ替えたり、省略したり、入れ替えたりする可能性がある
- インタラクションを含むブロックと静的な兄弟要素が混在しており、親コンポーネントの役割が不明瞭になっている
よく抽出されるもの:Meta、Actions、Media、Nav、および
リストやグリッドのアイテムコンポーネント。
Canvas 編集の観点
- 作成者が任意のブロックを領域に追加する必要がある場合は、コンポーネントの種類を列挙するプロパティではなく、スロットを使ってください。
- コピーが固定のマーケティング文で、組み合わせる必要がない場合は、文字列プロパティが適切です。
- 組み合わせる要素が、種類の変わらない任意のリッチブロック 1 つだけの場合は、内部に保持するか、多数の小さなスロットではなく単一のスロットを使うことを検討してください。
分割しない場合
次の場合は、コンポーネントをまとめたままにしてください。
- すべてのデザインバリアントで常に一緒に表示され、個別に存在する意味がない
- リフトアップすると扱いづらくなるほど、大きな内部状態を共有している
- 視覚的なデザインで緊密に結び付いている(たとえば、要素が重なり合う、背景を共有する、マスキングされる)
- 分割すると、他の場所では役に立たない 1~2 個のプロパティしか持たないコンポーネントができてしまう
分割せずにまとめる、または変換する目安
- 見出し 1 つ、または静的な段落 1 つだけをラップしており、別の箇所で使う予定がないコンポーネント
- 常に特定の種類の子要素を 1 つだけ受け取り、種類が変わらないスロット。構造が固定なら、親コンポーネントに統合するか、プロパティに置き換えてください。
- 1 つの要素に CSS を適用することだけを目的とするラッパーコンポーネント。そのラッパーを再利用する明確な理由がない限り、親のレイアウトやユーティリティクラスを優先してください。
アンチパターン
divごとにスロットを作る:作成者に実質的な制御を与えないまま、すべての flex 子要素をスロットにする。意味のある構成単位ごとにまとめてください。- プロパティのトグルを受け渡す:別々の子要素やスロットにすべき独立したブロックに、多数のブール値を使う
- 巨大なレイアウト:ページ全体のグリッドを 1 つのコンポーネントにハードコードする。1 つのモノリシックなページ外枠ではなく、レイアウトコンポーネントとネストした領域を優先してください。
再作業のループ
- 各失敗ノードを、粗すぎる、細かすぎる、または作成方法の不一致として分類します。
- 粗すぎるノードは、責務を分割し、可変領域用のスロットを導入するか、繰り返し項目を切り出します。
- 細かすぎるノードは、ノードを統合し、スロットをまとめるか、固定構造を親の実装内に移します。
- 境界が変わったノードだけ、props とスロットを再設計します。
- 合格するまで再監査するか、通常のルールが当てはまらない理由を含めて例外を記録します。
用語(任意)
atom、molecule、organism などの用語は、規模を表すために使うものであり、必ず従うべき要件ではありません。 厳密なアトミックデザインの階層よりも、再利用性、エディターによる制御、Canvas の制約を優先してください。