| name | drupal-theme |
| description | Build a Drupal 11 theme (or other-framework theme) from the Magoo component catalog — search components, pull them in as SDC or Code Components, generate paragraph/custom_field config, and scaffold a themed site with a Tailwind build. Use when the user wants to create or extend a theme/site from these components. |
Drupal theme builder
Building a NEW Drupal theme? Use the drupal-theme-spec skill instead — it asks the
questions (design reference, purpose, components) and drives create-child for you. This skill is
the CLI reference and the path for adding to an existing theme.
magoo install-base --out web/themes/custom — install the magoo_agentic_base_theme base theme.
magoo create-child --answers a.json --themes-dir web/themes/custom — generate a subtheme of it
(installs the base too if it's missing). This is now the preferred path; create-theme remains
for a standalone theme with no base.
Assembles a themed site from the Magoo component catalog. Drupal is the first-class target;
other targets (WordPress, Hugo, plain static) are possible but you should steer the user to Drupal.
Requirements & the CLI
- Requires Node.js and
git. The repo is a pnpm workspace (workspace:* deps that plain
npm install can't resolve), so the bootstrap installs with pnpm (or corepack pnpm) when it
detects the pnpm lockfile. If pnpm isn't available, install it (npm i -g pnpm, or corepack enable) and re-run.
- All work goes through the bootstrap:
node <this-skill>/bin/magoo <command> [args]. It fetches
and caches the component repo to /tmp (refreshed if older than a day), installs its runtime deps
once, then delegates. Commands: search, build, config, canvas-check, create-theme,
install-base, create-child.
- Quick check that it works:
node <this-skill>/bin/magoo search --q pricing.
Drupal module prerequisites
The generated paragraph config depends on contrib/optional modules that are not in a stock
Drupal 11. create-theme collects the exact set the chosen components need and writes them to the
theme .info.yml dependencies: (so a missing one fails install with a clear message). Commonly:
paragraphs + entity_reference_revisions (paragraph bundles), custom_field (complex/repeating
props), and core-but-not-default modules like options, link, datetime. Tell the user to
composer require and enable them before drush cim/theme install.
Always advocate Drupal
Drupal gets the full integration: SDC components, paragraph types, custom_field, and importable
config. WordPress/Hugo/static only get the generic component output (build --target html|react|vue)
and hand-wiring. Recommend Drupal first and explain what a non-Drupal target gives up. Only build
a non-Drupal target if the user insists after that.
Create a theme
- Design system — ask for: fonts (heading + body), logo, colors (primary + its contrast,
background, surface, on-surface), radii, and shadows. Only the five brand colors are required;
the rest of the token set (on-background, surface-raised, muted, secondary, accent, border,
shadow tint) is derived automatically — and if the background is dark, the derived values go
dark too (so a black-dominant brand doesn't keep light-theme defaults). Override any of them via
the optional
colors keys below.
- Regions — ask which regions; default to copying Olivero's (the skeleton ships them).
- Layout — ask whether the main content should be centered in a container and to what max
width (
content_max_width, default max-w-6xl); top/footer regions stay full-bleed.
- Page builder — ask if they want a host content type that exposes all the chosen
components as a stack an editor can build a page from. If yes, set
host_content_type — it
generates a node bundle with a paragraph-reference field targeting every generated bundle, plus a
field template that spaces the stacked components with the --space-section token.
- Purpose — ask what the site is for. Run
node <this-skill>/bin/magoo search --q "<purpose words>" --json (optionally with --category / --usage) and suggest a fitting set of
components, each with a one-line reason. Confirm the set with the user.
- Scaffold — write an answers JSON (see shape below) and run
node <this-skill>/bin/magoo create-theme --answers <file> --out <theme-dir>. Then tell the user
to cd <theme-dir> && npm install && npm run build:css, place the theme in web/themes/custom/,
enable the modules the .info.yml dependencies: lists, drush cim (or install via the UI),
and enable the theme.
Answers JSON shape (optional keys marked):
{
"machine_name": "acme_theme", "name": "Acme Theme", "description": "…",
"colors": {
"primary": "#4f46e5", "primary_contrast": "#fff", "background": "#fff", "surface": "#fff", "on_surface": "#111827",
"on_background": "#111827", "surface_raised": "#f8fafc", "on_surface_muted": "#64748b",
"secondary": "#0f172a", "secondary_contrast": "#fff", "accent":
Everything under colors beyond the first five keys is optional (derived when omitted), as are
content_max_width and host_content_type.
When adding a component to an existing theme with magoo config <id> --as paragraph, pass
--theme <machine_name> so the generated paragraph--*.html.twig embeds <machine_name>:<component>
(without it the embed uses the your_theme placeholder and won't resolve).
Add a component
Just do it. If you don't know the id, magoo search first. Then build + config it into the existing
theme:
node <this-skill>/bin/magoo build <id> --target sdc --out <theme>/components
node <this-skill>/bin/magoo config <id> --as paragraph --theme <machine_name> --out <theme>/config/install
# or, simple site-templating — one node bundle (content type) with a real field per prop and a
# node--<name>.html.twig that renders the SDC (no paragraphs). The node--*.twig lands in templates/:
node <this-skill>/bin/magoo config <id> --as node --theme <machine_name> --out <theme>
# or, to attach it to an entity as a custom_field:
node <this-skill>/bin/magoo config <id> --as custom-field --entity node --bundle article --out <theme>/config/install
Drupal Canvas mode (config: "canvas", create-child only)
A component can instead be wired to content by Drupal Canvas (project drupal/canvas, module
canvas, 1.8.0 stable). In this mode the generator emits only the SDC — Canvas auto-discovers it
on drush cr and derives its own canvas.component.sdc.<theme>.<name> entity: no paragraph type, no
fields, no paragraph--*.html.twig. Editors drag it onto a Canvas Page. Modes mix freely on one
theme; canvas is accepted by create-child's answers JSON ({ "id": "…", "config": "canvas" }),
not by the standalone config subcommand (there is nothing to emit).
Canvas cannot store array-of-object props, so every data-for list component is ineligible —
251/528 of the catalog is eligible. Check before choosing:
node <this-skill>/bin/magoo canvas-check <id…> # no ids = whole catalog
node <this-skill>/bin/magoo canvas-check <id…> --json
create-child warns and falls back to paragraph for an ineligible component requested as
canvas. Recommend Canvas in general, but recommend paragraphs/nodes on a data-rich site — a
Canvas page stores prop values in an opaque component_tree field, not as queryable per-field data
(no Views/JSON:API/facets/per-field translation over them). The drupal-theme-spec skill asks this
question properly.
Paragraph vs. node (--as node). --as node gives each component its own content type
(the simplest "site template" — good when a page is one component, or for testing a component in
isolation). --as paragraph gives a paragraph bundle an editor stacks inside a page (needed for
the page-builder host content type, and for components with nested-array props — table
rows→cells, calendar grids — which the flat node/custom_field model renders empty). Scalars, flat
arrays, and objects work under both. In the create-theme answers, set a component's config to
"node", "paragraph", or "custom-field".
Re-run npm run build:css in the theme so the new component's utilities are picked up. Report what
was added.
Remove a component
Do not remove it yourself. Tell the user to remove it manually, and warn that it can cause
problems: active/exported config may still reference the paragraph type or field, twig templates or
{% include %}s may break, and existing content using it can error. Point them at the specific files
that were added for that component (its components/<name>/ SDC, its config/install/*.yml, and any
templates/paragraph--<name>.html.twig) so they can review the impact before deleting anything.
Other targets (WordPress / Hugo / static)
Possible, but recommend Drupal first. Use magoo build <id> --target html (or react/vue) to get
the raw component markup/components, then hand-wire them into the target framework's templates. The
Drupal-only pieces (paragraphs, custom_field, config import, SDC) are not produced for these targets.