skills/canvas-content-templates/SKILL.md
Create and modify content templates that map Drupal content entities to Canvas component layouts. Use when asked to (1) Create a new content template, (2) Modify an existing content template, (3) Add or change entity field mappings in a template, (4) Compose components in a content template via slots. Content templates live in the configured `contentTemplatesDir` (default `content-templates/`) and define how Drupal entity types, bundles, and view modes render as Canvas component trees.
npx skillsauth add drupal-canvas/skills canvas-content-templatesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A content template is a JSON file that maps a Drupal content entity (identified by entity type, bundle, and view mode) to a tree of Canvas component elements. Content templates define how Drupal content is rendered using Canvas components, including which entity fields map to which component props.
Author content templates in the configured content templates directory
(contentTemplatesDir in canvas.config.json). If contentTemplatesDir is not
set, the default is the top-level content-templates/ directory.
Naming convention: {entityType}.{bundle}.{viewMode}.json
Examples:
<content-templates-dir>/node.article.full.json<content-templates-dir>/node.article.teaser.jsonEach content template is a JSON object with these top-level keys:
label (required): human-readable description of the template (e.g.,
"Article — Full content")entityType (required): the Drupal entity type (e.g., "node")bundle (required): the content type / bundle (e.g., "article")viewMode (required): the view mode (e.g., "full", "teaser")elements (required): an object of component elements keyed by element IDThe filename must match {entityType}.{bundle}.{viewMode}.json and correspond
to the values inside the file.
The elements shape is identical to Canvas page specs — see the
canvas-page-definition skill for examples. The only difference is prop value
types: content templates additionally support prop sources that pull from the
host Drupal entity (described below).
Each entry in elements is keyed by an element ID and contains:
type (required): the Canvas component type, prefixed with js. (e.g.,
"js.hero", "js.card", "js.section")props (optional): component props — can be static values or entity field
referencesslots (optional): a map of slot names to arrays of element IDs from the same
templateUse stable, descriptive element IDs unless you are intentionally preserving IDs from another source such as an exported Canvas content template.
Props can hold three kinds of values:
Plain JSON values — strings, numbers, booleans, or objects with value and
format for rich text.
{
"buttonLabel": "Build your hybrid team",
"darkVariant": true,
"description": {
"value": "Some <em>HTML</em> content here.",
"format": "canvas_html_block"
}
}
Dynamic values pulled from the Drupal entity. Use sourceType: "entity-field"
with an expression string.
{
"title": {
"sourceType": "entity-field",
"expression": "ℹ︎␜entity:node:article␝title␞␟value"
}
}
Links back to the entity's canonical URL. Use sourceType: "host-entity-url".
{
"link": {
"sourceType": "host-entity-url",
"absolute": false
}
}
absolute (optional, defaults to true) controls URL form: false emits a
relative path (/node/123), true emits a fully qualified URL
(https://example.com/node/123). Set absolute: false for in-page links; omit
or set true for canonical links, feeds, or anything consumed off-site.
Expressions use special Unicode control characters as delimiters:
ℹ︎␜entity:{entityType}:{bundle}␝{fieldName}␞␟{property}
| Character | Name | Purpose |
| --------- | --------------- | ----------------------- |
| ℹ︎ | info | Expression start |
| ␜ | file separator | Segment separator |
| ␝ | group separator | Entity/field boundary |
| ␞ | record sep. | Field/property boundary |
| ␟ | unit separator | Property start |
For a single property from a field:
ℹ︎␜entity:node:article␝title␞␟value
ℹ︎␜entity:node:article␝body␞␟processed
For image fields that need multiple properties, use the multi-property syntax
with ↠ (rightwards arrow) mapping source properties:
ℹ︎␜entity:node:article␝field_image␞␟{src↠src_with_alternate_widths,alt↠alt,width↠width,height↠height}
The {prop↠source,...} syntax maps component prop keys to entity field
properties.
To traverse entity references (e.g., author's profile picture), chain entity
segments with ␜␜:
ℹ︎␜entity:node:article␝uid␞␟entity␜␜entity:user␝user_picture␞␟{src↠src_with_alternate_widths,alt↠alt,width↠width,height↠height}
This follows: node → uid field → referenced user entity → user_picture field.
Prop sources describe which Drupal entity fields can be mapped to which component props. When available, they are the authoritative reference for building entity field mappings — always prefer them over manually constructing expressions.
Prop sources are stored in .agents/drupal-canvas/prop-sources.json, organized
as {entityType} → {bundle} → {js.component} → {propName} → array of
source options. Each source option has:
label: human-readable description of the field (e.g., "Title", "Image",
"Body")source: the prop value object to use directly in the content template —
either { "sourceType": "entity-field", "expression": "..." } or
{ "sourceType": "host-entity-url", "absolute": false }How to use prop sources:
node.article, node.page)js.card, js.hero) tell you which components have mappable props for that
bundlesource object
directly into the content template's prop value — do not manually construct
entity field expressions when a matching source existslabel to pick the semantically correct one (e.g., for a
card's heading prop, choose "Title" over "Alternative text")When prop sources are not available, follow the freshness check in the workflow section to refresh agents context. Fall back to manually constructing entity field expressions only when no matching source exists.
View modes define the valid entity type, bundle, and view mode combinations on the connected Drupal site. Use them to determine which content templates can be created and which ones already exist.
View modes are stored in .agents/drupal-canvas/view-modes.json, organized as
{entityType} → {bundle} → {viewMode} → label string (e.g.,
"Full content", "Teaser").
How to use view modes:
When view modes are not available, follow the freshness check in the workflow section to refresh agents context. If still not available, ask the user which view mode to target.
.agents/drupal-canvas/prop-sources.json and
.agents/drupal-canvas/view-modes.json exist. If either file is missing or
was last modified more than 1 hour ago, run npx canvas agents-context to
refresh. If the files are under 1 hour old, skip the refreshsrc/components/ that
will compose the layout. Use component.yml files to understand each
component's props and slots. Cross-reference with available prop sources to
confirm which props can be mapped to entity fields# Check freshness of agents context files and refresh if missing or older than 1 hour.
# Use `date -r <file>` to check file modification time (works on macOS and Linux).
# If stale or missing:
npx canvas agents-context
# Check available view modes
cat .agents/drupal-canvas/view-modes.json
# Check existing templates (using configured contentTemplatesDir, default: content-templates/)
ls content-templates/
# Read prop sources for the target bundle (prefer jq when available)
jq '.node.article' .agents/drupal-canvas/prop-sources.json
# Check available components
ls src/components/
# Read a component's props to know what to map
cat src/components/hero/component.yml
elementsValidate every authored content template before finishing.
npx canvas validate
.agents/drupal-canvas/*.json files are internal agent context. Don't mention
them, their paths, or their contents in summaries, commit messages, or other
user-facing output — describe what was mapped, not where the mapping came from.
testing
Use for any task touching site chrome — header, footer, sidebar, or global navigation that repeats across pages — and for any region-spec work (create, modify, review, validate region JSON, or the project-level layout component). Also load when a task creates or edits multiple pages that share chrome, or asks for a "site" or "navigation between pages"; shared chrome belongs in regions, never inlined into page JSON.
tools
Plans and builds Drupal Canvas navigation UI (main nav, footer links, sidebar nav, mobile drawers, breadcrumbs) using design decomposition for structure and props/slots, then JSON:API menu or page-context patterns from canvas-data-fetching. Use when the user asks for navigation, header or footer links, menus, menu_items, mobile nav, or breadcrumb trails. Run after canvas-design-decomposition for layout and API sketches; follow canvas-data-fetching for SWR, JsonApiClient, sortMenu, and menu fallbacks.
development
Plans structure for a component library with props/slots and right-sized component granularity. Run before building or adding Canvas components (new `src/components/` folders, component.yml, React), or for plan-only / breakdown-only work, whenever UI must map to a coherent tree. Mandatory for every new Figma frame or greenfield screen—repository drafts do not replace phases A–G.
development
Use when work must be verified in local Canvas Workbench, or when the user asks to run, open, check, or author component mocks or page previews in Workbench. Verifies that Canvas Workbench is available through the project's package runner, starts the local Workbench dev server, and keeps Workbench verification as part of the implementation workflow.