新しいPaletteコンポーネントの作成
Single Directory Component(SDC)をPaletteに追加するための実践ガイド。各コンポーネントに必ず含めるべきものと、Style(バリアント)設定の仕組みを解説します。アーキテクチャの概要についてはCLAUDE.mdを、styles.mdを、別のモジュール/テーマからコンポーネントを拡張する方法については参照してください。
1. コンポーネントの構造
各コンポーネントは、components/以下の独自フォルダに配置されます:
components/<id>/
<id>.component.yml # required — metadata + prop/slot schema
<id>.twig # required — the markup
<id>.css # optional — co-located styles
<id>.js # optional — co-located behaviour<id>はマシン名(小文字、アンダースコア)です。Paletteはコンポーネントをcanvas_palette:<id>として公開します。- コアのSDCは、フォルダを自動検出し、ライブラリを自動生成します
core/components.canvas_palette--<id>。同じ場所に配置した.css/.jsから生成されます。ライブラリを自分で登録する必要はありません。
1a. <id>.component.yml — 必須キー
$schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json
name: My Thing # human label shown in the editor
group: Palette # category in the component list — always "Palette"
status: experimental # experimental | stable | deprecated
description: 'One line describing what it does.'
props:
type: object
properties:
# … see below …
slots:
# … optional, see 1c …1b. プロパティ(props)
各propはJSONスキーマのプロパティです。このキットでよく使う形は次のとおりです:
props:
type: object
properties:
title:
type: string
title: Title
examples: ['Default title'] # the editor placeholder / seed value
text:
type: string
title: Text
contentMediaType: text/html # rich text …
x-formatting-context: block # … edited with the block CKEditor
examples: ['<p>Some copy.</p>']
count:
type: integer
title: Count
default: 3
image:
$ref: json-schema-definitions://canvas.module/image
type: object
title: Image重要なルール(すべてPaletteによって強制されます。間違えるとコンポーネントは黙って自身を無効化し、エディタから消えます):
required:プロパティには必ずexamples値が必要です。デフォルトが不要な場合は、空のexampleを与えるのではなく、propをオプションにします(required:から外す)。examples[0]は、新しく配置したインスタンスに初期値として設定される値です。エディタのデフォルト/プレースホルダーとして扱ってください。default:は、propが未設定のときにTwigが参照するランタイムのフォールバックです。- Enum propは、人間向けラベルとして
meta:enumマップを列挙します(§2参照)。 - オプションのintegerpropは、エディタでクリアするとエラーになります。クリア可能にするのではなく、
default: 0を用いて、Twig側で「未設定」として扱うのが望ましいです。
1c. スロット(コンテナコンポーネント)
コンテナコンポーネント(Carousel、Tiles、Stats…)は、子コンポーネントを静的なslotsに保持し、通常は専用の*_itemコンポーネントとペアにします:
slots:
items:
title: Items
description: 'Place one My Item per row.'
expected: ['canvas_palette:my_item']
minItems: 1スロットはTwigで{% block <slot> %}{% endblock %}を使ってレンダリングされます。Paletteはランタイムで動的なスロットを禁止しているため、スロット数は定義時に固定されます。
1d. <id>.twig
通常のSDCのTwigです。propとスロットはトップレベルの変数/ブロックになります:
<div class="cp-my-thing cp-my-thing--{{ style|default('default') }}">
{% if title %}<h2 class="cp-my-thing__title">{{ title }}</h2>{% endif %}
{% if text %}<div class="cp-my-thing__body">{{ text }}</div>{% endif %}
{% block items %}{% endblock %}
</div>規約: BEMクラス名cp-<id>、cp-<id>__element、cp-<id>--modifier。
1e. Drupal.behaviors/once/サードパーティライブラリを使うJS
自動生成されるコンポーネントライブラリにcore/drupalが注入されるのは、コンポーネントがlibraryOverridesを宣言している場合のみです。そのため、.jsがDrupal.behaviors、once()、または共有ライブラリを使うコンポーネントは、依存関係を宣言する必要があります:
libraryOverrides:
dependencies:
- core/drupal
- core/once
- canvas_palette/glightbox # third-party libs are attached the same wayこれがないと、Drupal/onceは実行時にundefinedになります。(tabs、accordion、carouselの実例を参照してください。)
1f. エディタ専用のUI要素
Paletteエディタのプレビュー内だけで何かを表示する(グラブ用パディング、空の輪郭など)には、Paletteがtwigコンテキストに注入するcanvas_is_previewフラグで制御し、cp-<id>--previewモディファイアを出力します。これは公開ページには決して届きません:
{% set classes = ['cp-my-thing', canvas_is_preview ? 'cp-my-thing--preview' : ''] %}JS側の同等の方法はwindow.frameElement?.dataset?.canvasPreview === 'true'です。
2. Style設定の仕組み
「Style」設定は、コンポーネントのバリアントに対するPalette唯一の規約です。スタイルごとのTwigやJSの分岐はありません — 1つのenum propをCSSクラスに変換するだけです。
2a. styleprop
ほとんどのコンポーネントは、styleという名前の単一のenum文字列propを宣言します(ラベルは「Style」、ときに「Layout」/「Columns」):
style:
type: string
title: Style
default: default
enum:
- default
- colorful
meta:enum:
default: Default
colorful: Colorful
examples: [default]enum— マシン値。meta:enum— セレクトに表示される人間向けラベル。default/examples— 開始値。
2b. TwigがBEMモディファイアに補間する
Twigはその値をそのままモディファイアクラスとしてルート要素に書き込みます:
<div class="cp-my-thing cp-my-thing--{{ style|default('default') }}">つまり、style: colorfulはclass="cp-my-thing cp-my-thing--colorful"をレンダリングします。
2c. CSSはモディファイアをキーにする
各スタイルは、.cp-<id>--<value>をキーにした単なるCSSルールセットです:
.cp-my-thing--colorful {
background: linear-gradient(135deg, #0973f2, #7333e5);
color: #fff;
}これがすべての仕組みです: enum値 → cp-<id>--<value>クラス → CSS。
2d. エディタフォームの先頭に浮上する
canvas_palette_move_style_prop_first()は、styleという名前のpropに非常に低い#weightを与えるので、Styleセレクタは常にコンポーネント入力サイドバーの先頭に表示されます。これはcanvas_palette_form_component_instance_form_alter()から、すべてのsdc.canvas_palette.*コンポーネントに対して実行されます。propにstyleと名付けるだけで自動的に適用されます。(propを再重み付けするカスタム入力フォームハンドラを持つコンポーネントも、styleを先頭に残す必要があります。)
2e. 既存コンポーネントに新しいStyle値を追加する
enum値の追加は、追加的で安全な変更です:
- 値を
enum:に、ラベルをmeta:enum:に追加します。 .cp-<id>--<value>のCSSを追加します。- リビルドします(§3参照)。新しい値は、新しく配置したインスタンスと、エディタのハードリロード後に表示されます。既存のインスタンスは固定されたバージョンを保持します。
このキットで共有されるスタイル規約(正確なトークン値はCLAUDE.md参照):colorful値と、オプションのeyebrowキッカーprop。ランディングページのデザインシステムに合わせてスタイル設定されています。新しい色を考案せず、そこにあるハードコードされたトークン値を再利用してください。
3. ビルドとバージョニングの注意点
コンポーネントを追加・編集したら、サイトルートからリビルドします:
php vendor/drush/drush/drush.php crこのOSPanelスタックでは、CLIのdrush crが届かないことがよくあります。Web経由でフラッシュしてください(管理画面のConfiguration → Development → Performance → Clear all caches、または使い捨てのweb/flush.php)。CLAUDE.mdの「Local environment quirk」を参照してください。
Canvasは各コンポーネントのスキーマを、バージョン管理された設定エンティティ(sdc.canvas_palette.<id>)として凍結します。その結果:
*.component.ymlを編集しても、配置済みのインスタンスはライブ更新されません。 新しいprop/enum値は、新しく配置したインスタンスと、エディタのハードリロード後に表示されます。- 追加的な変更は安全です(新しいenum値、新しいオプションprop)。既存propのフィールド型を変更するのは安全ではありません — エラーを投げ、コンポーネント全体を無効化します。型を元に戻すと復元されます。
変更後にコンポーネントが無効化された場合は、再生成して再有効化します:
\Drupal::service('Drupal\canvas\ComponentSource\ComponentSourceManager') ->generateComponents(); $c = \Drupal::entityTypeManager()->getStorage('component') ->loadUnchanged('sdc.canvas_palette.<id>'); if ($c && !$c->status()) { $c->enable()->save(); }コンポーネントが2回目の生成でも有効なままであることを確認してください。手動での再有効化1回しか持たない修正は、本当の修正ではありません。
新しいコンポーネントのチェックリスト
components/<id>/<id>.component.yml($schema、name、group: Palette、status、descriptionを含む)。
propを定義。すべてのrequiredpropにexamplesがある。
<id>.twig(cp-<id>のBEMクラスを含む)。
styleenum prop(+meta:enum)をバリアントがある場合に用意し、cp-<id>--{{ style }}と対応するCSSに接続する。
libraryOverrides.dependencies(JSがDrupal.behaviors/once/サードパーティライブラリを使う場合)。
スロット+ペアの*_itemコンポーネント(コンテナの場合)。
(Web経由で)リビルド済み。コンポーネントが2回の生成にわたってENABLEDと表示される。