canvas-component-metadata について
canvas-component-metadata は、Drupal Canvas コンポーネントのメタデータを扱う開発スキルです。component.yml と props・スロットの規則に加え、画像やエンティティ参照のスキーマ、列挙値と実装の対応を説明します。
主な機能
- 01component.yml のトップレベル構造
- 02props の型と定義要件
- 03スロットのモデリング方法
- 04列挙値と実装バリアントの一致要件
活用シーン
- 01component.yml の作成や変更
- 02Canvas で編集可能な props とスロットの設計
- 03props の検証や列挙設定の問題解決
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ drupal-canvas/ skills --skill canvas-component-metadataインストール後、Claude Code で/canvas-component-metadataを入力して読み込まれたか確認します。
詳細
プロジェクトの種類
このスキルを適用する前に、package.json に @drupal-canvas/headless または
@drupal-canvas/headless- で始まる依存関係があるか確認してください。該当する依存関係が
ある場合は、Canvas Headless のコードベースです。まず
canvas-headless を読んでください。以下の
React 固有のガイダンスよりも、そちらが優先されます。このスキルの
component.yml スキーマは、ヘッドレスプロジェクトでも同一です。該当する依存関係が
ない場合は、Canvas でレンダリングされる React のコードベースです。コンポーネントは
React(index.jsx/.tsx)であり、このスキルの内容がすべてそのまま適用されます。
プロジェクトの種類はこの2つのみです。
ファイル構造
すべての component.yml に、次のトップレベルキーを含める必要があります。
name: Component Name # Human-readable display name
machineName: component-name # Machine name in kebab-case
status: true # Whether the component is enabled
required: [] # Array of required prop names
props:
properties:
# ... prop definitions
slots: {} # Use an empty object when there are no slots
Props
要件
すべてのプロップ定義には、UI ラベルとして title が必要です。必須プロップには
examples 配列が必須であり、それ以外のプロップにも指定することを推奨します。
Drupal Canvas では、最初の例の値のみが使用されます。
プロップが required に含まれている場合、そのプロップにはコンポーネントの実装内で
フォールバック値やデフォルト値を設定しないでください。必須の Canvas プロップは、
実装側で暗黙のデフォルト値を設定するのではなく、メタデータまたはエディター入力から
指定してください。
props:
properties:
heading:
title: Heading
type: string
examples:
- Enter a heading...
// Correct: required prop has no fallback default
const Hero = ({ heading }) => <h1>{heading}</h1>;
// Wrong: required prop fallback masks missing required data
const Hero = ({ heading = 'Default heading' }) => <h1>{heading}</h1>;
プロップ ID は、タイトルをキャメルケースにした形式にしてください。
プロップ ID(properties の下にあるキー)は、title の値をキャメルケースに変換したものに
してください。
component.yml には、ユーザー向けで Canvas から編集可能なプロップのみを含めてください。
実装専用のプロップは実装内にとどめ、メタデータには追加しないでください。
className は component.yml に絶対に含めないでください。 開発者向けの
コンポジション用プロップとして扱い、Canvas エディターのコントロールにはしないでください。
# Correct
props:
properties:
buttonText: # camelCase of "Button Text"
title: Button Text
type: string
backgroundColor: # camelCase of "Background Color"
title: Background Color
type: string
isVisible: # camelCase of "Is Visible"
title: Is Visible
type: boolean
# Wrong
props:
properties:
btn_text: # should be "buttonText" for title "Button Text"
title: Button Text
bgColor: # should be "backgroundColor" for title "Background Color"
title: Background Color
プロップの種類
テキスト
基本的なテキスト入力です。文字列値として保存されます。
type: string
examples:
- Hello, world!
書式付きテキスト
HTML の書式設定に対応したリッチテキストコンテンツで、ブロックコンテキスト内に表示されます。
type: string
contentMediaType: text/html
x-formatting-context: block
examples:
- <p>This is <strong>formatted</strong> text with HTML.</p>
リンク
内部または外部のリソースへのリンクに使用する URL または URI の参照です。
type: string
format: uri-reference
examples:
- /about/contact
注: フォーマットには、絶対 URL のみを受け付ける uri と、絶対 URL と相対 URL の
両方を受け付ける uri-reference を指定できます。
重要:URL プロップには適切なパスの例を使用してください。 uri-reference プロップの
値の例に # を使用しないでください。プッシュ時に検証エラーが発生することがあります。
必ず、実際のパスに即した例を使用してください。
# Correct
examples:
- /resources
- /about/team
- https://example.com/page
# Wrong
examples:
- "#"
- ""
画像
代替テキスト、寸法、ファイル URL などのメタデータを含む画像オブジェクトへの参照です。
1つの画像を表すプロップには、この形式を使用してください。単一の画像を
imageUrl、imageAlt、imageWidth、imageHeight のような個別のプロップに分けて
モデル化しないでください。
image、backgroundImage、logoImage、cardImage など、意味が明確なプロップ ID を
使用してください。ただし、値の形式は同じにし、type: object と Canvas の画像スキーマ
参照を指定してください。
type: object
$ref: json-schema-definitions://canvas.module/image
examples:
- src: >-
https://images.unsplash.com/photo-1484959014842-cd1d967a39cf?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80
alt: Woman playing the violin
width: 1770
height: 1180
URL フィールドと代替テキストのフィールドを分けず、1つの画像オブジェクトとしてプロップを モデル化してください。
# Correct
props:
properties:
image:
title: Image
type: object
$ref: json-schema-definitions://canvas.module/image
examples:
- src: https://example.com/photo.webp
alt: Product hero image
# Wrong
props:
properties:
imageUrl:
title: Image URL
type: string
format: uri-reference
imageAlt:
title: Image Alt
type: string
コンテンツエンティティ参照
選択した Drupal コンテンツエンティティへの参照を、構造化オブジェクトとして表します。コンポーネントでエンティティを選択するよう編集者に求め、選択したエンティティのフィールドをまとめて 1 つの prop で JSX に渡す場合に使用します。
props:
properties:
featuredArticle:
title: Featured Article
type: object
$ref: json-schema-definitions://canvas.module/content-entity-reference
コンテンツエンティティ参照の props は省略可能でなければなりません。required には記載しないでください。x-allowed-entity-type-id や x-allowed-bundle は記述しないでください。Canvas は dataDependencies.entityFields からこれらの値を取得します。
コンテンツエンティティ参照の props に examples を追加しないでください。プレビュー値は dataDependencies.entityFields から解決されます。
.agents/drupal-canvas/content-entity-reference-expressions.json の 1 つ以上の式を dataDependencies.entityFields.<propName> に追加します。
.agents/drupal-canvas/content-entity-reference-expressions.json が存在しない、古い、または必要なエンティティフィールドが含まれていない場合は、npx canvas agents-context cer-expressions を実行して、設定済みの Canvas サイトからコンテンツエンティティ参照の式のみを更新します。
dataDependencies:
entityFields:
featuredArticle:
- ℹ︎␜entity:node:article␝title␞␟value
- ℹ︎␜entity:node:article␝path␞␟alias
1 つのコンテンツエンティティ参照 prop の式は、すべて同じエンティティタイプとバンドルを対象にする必要があります。式の名前だけから、開発者が使用する最終的なオブジェクトの形を推測しないでください。コンポーネントのメタデータがローカルに存在するようになったら、npx canvas agents-context cer-preview <component> を実行して、JSX で使用する実際に解決された prop の形を確認します。参照式によってネストされたオブジェクトが作成される場合があり、__type などのメタデータキーが含まれることもあります。
動画
寸法やファイル URL などのメタデータを持つ動画オブジェクトへの参照です。必須なのはファイル URL のみです。それ以外のメタデータはすべて省略可能です。
type: object
$ref: json-schema-definitions://canvas.module/video
examples:
- src: https://media.istockphoto.com/id/1340051874/video/aerial-top-down-view-of-a-container-cargo-ship.mp4?s=mp4-640x640-is&k=20&c=5qPpYI7TOJiOYzKq9V2myBvUno6Fq2XM3ITPGFE8Cd8=
poster: https://example.com/600x400.png
ブール値
真または偽の値です。
type: boolean
examples:
- false
整数
小数部分のない整数値です。
type: integer
examples:
- 42
数値
小数部分を含めることができる数値です。
type: number
examples:
- 3.14
リスト:テキスト
ユーザーが選択できる、あらかじめ定義されたテキストの選択肢一覧です。
type: string
enum:
- option1
- option2
- option3
meta:enum:
option1: Option 1
option2: Option 2
option3: Option 3
examples:
- option1
リスト:整数
ユーザーが選択できる、あらかじめ定義された整数の選択肢一覧です。
type: integer
enum:
- 1
- 2
- 3
meta:enum:
1: Option 1
2: Option 2
3: Option 3
examples:
- 1
列挙型
列挙型の値には、小文字の機械可読な識別子を使用する必要があります。meta:enum を使用して、UI に表示する人間が読みやすいラベルを指定します。
注: 列挙型の値にピリオドを含めることはできません。
# Correct
enum:
- left_aligned
- center_aligned
meta:enum:
left_aligned: Left aligned
center_aligned: Center aligned
examples:
- left_aligned
# Wrong
enum:
- Left aligned
- Center aligned
examples の値には、表示ラベルではなく列挙型の値を指定する必要があります。
列挙型の値は実装のバリアントキーと一致させる必要があります
実装で列挙型 props をバリアントマップ(class-variance-authority(CVA)または通常のバリアントオブジェクト)を通じてスタイルにマッピングする場合、バリアントキーは component.yml で定義された列挙型の値と完全に一致する必要があります。
// component.yml defines: enum: [left_aligned, center_aligned]
// CVA variants must match:
const variants = cva('base-classes', {
variants: {
layout: {
left_aligned: 'text-left', // matches enum value
center_aligned: 'text-center', // matches enum value
},
},
});
スロット
スロットを使うと、他のコンポーネントをコンポーネント内に埋め込めます。実装でスロットを使用する方法は、フレームワークによって異なります。
- React: 各スロットは、スロットキーと一致する名前付き prop として受け取ります。
- Vue と Astro(ヘッドレスプロジェクト): 各スロットは、名前付きスロット
<slot name="slotKey" />としてレンダリングされます。
このセクションをスロットスキーマの信頼できる唯一の情報源とします。他のスキルでは、スロットスキーマの詳細を再定義せず、ここに記載されたルールを参照してください。
スロットを作成する前に、ユースケースが明らかにコンポジションを目的とする場合(たとえば、リッチなネストコンテンツや、繰り返し埋め込むコンポーネント)を除き、ユーザーに確認してください。シンプルなテキスト形式の値には、prop を優先してください。
重要: Canvas のスロットを、デフォルトで children prop にマッピングしないでください。スロットキーが content の場合は、JSX でも content として使用してください。
スロットキーに children という名前を使うことは技術的には可能ですが、スロット名はユーザーに表示される Canvas のラベルに反映されることが多いため、推奨しません。content、media、actions など、明確なスロットキーを優先してください。
slots は、スロット名(content、sidebar など)をキーとするオブジェクトマップにする必要があります。コンポーネントにスロットがない場合は、空のオブジェクトを使用してください。
slots:
content:
title: Content
buttons:
title: Buttons
React コンポーネントでは、スロットを名前付き props として分割代入し、直接レンダリングします。
const Section = ({ width, content }) => {
return <div className={sectionVariants({ width })}>{content}</div>;
};
// Wrong when the slot key is `content`: this does not consume the named slot.
const Section = ({ children }) => {
return <div>{children}</div>;
};
コンポーネントにスロットがない場合は、slots: {} を使用してください。
slots: {}
スロットオブジェクトの配列は使用しないでください。
# Wrong
slots:
- name: content
title: Content