SpectreSkills
← All recipes

cms-page-builder

v1.0.0

Build a Shopify OS 2.0-style page builder end to end — a zero-dependency theme contract, a first-party section catalog, imported sections that are data not code, pages as Content beside the theme, a customizer over the live renderer, a host-keyed publish seam, and tenancy that never creates a workspace from a host header. Combine when the brief is a visual CMS, theme editor, or multi-tenant landing-site product.

CMS & Page Builderscmspage-builderthemecustomizermulti-tenant
Try it — copy this prompt into Claude Code
Run `npx @spectre-apps/skills recipe cms-page-builder` to install the skills, then use them in order to build me a visual page builder: a ThemeBundle of primitive settings in a zero-dependency contract package, a first-party section catalog of four artifacts each, an importer that turns a Figma frame into a tag-allowlisted template tree, pages as workspace-scoped rows that fall back to the default layout, a three-pane customizer over a live cross-origin preview, a public renderer that never touches the database and caches per host, and tenancy where Clerk authenticates a person and membership is ours.

One paste installs the skills and kicks off the whole build.

Install the whole recipe
npx @spectre-apps/skills recipe cms-page-builder

Installs all 7 skills into .claude/skills. Add --user to install globally.

Or add them individually
npx @spectre-apps/skills add cms-theme-contract cms-section-catalog cms-imported-sections cms-pages-and-templates cms-customizer cms-publish-and-seam cms-tenancy

7 skills, in order

  1. 01
    cms-theme-contractv1.0.0

    Model a visual CMS as a Shopify OS 2.0-style theme contract — a ThemeBundle of primitive settings, JSON templates, and section instances, normalized on every read, in a zero-dependency package both the editor and the public renderer import. Trigger when building or extending a page builder, theme engine, or customizer, or when deciding what lives in the bundle versus beside it.

  2. 02
    cms-section-catalogv1.0.0

    Declare every first-party CMS section as four artifacts — a manifest, a React component, a registry entry, and a preset — with settings, blocks, CTAs, and spacing generated from the manifest. Trigger when adding or editing a section in a page builder, or when mapping a design onto a section contract.

  3. 03
    cms-imported-sectionsv1.0.0

    Turn a customer design (Figma, HTML, a Claude Design export) into an editable CMS section that is DATA, not code — a tag-allowlisted template tree, CSS scoped on write, types namespaced custom:<id>, snapshotted at publish. Trigger when importing a design into a page builder, building a section importer, or deciding how user-authored sections render.

  4. 04
    cms-pages-and-templatesv1.0.0

    Split CMS pages from the theme — a page is a workspace-scoped row (Content) and the theme supplies only the layout it renders through (Theme), with variant fallback to default so a theme swap never 404s a live URL. Trigger when adding pages, templates, or handles to a page builder, or when deciding whether something belongs in the bundle.

  5. 05
    cms-customizerv1.0.0

    Drive a CMS theme editor as a three-pane customizer over a live cross-origin preview — query-marker preview (not a cookie), a theme-scoped token, load-bearing data-section-id hooks, and both render paths sharing every resolver. Trigger when building or extending a theme editor, visual customizer, or in-canvas section inspector.

  6. 06
    cms-publish-and-seamv1.0.0

    Publish a CMS theme as an append-only snapshot and serve the public site over a bearer-gated read seam the renderer caches per HOST — the site never touches the database, and a shared cache key is a cross-tenant leak. Trigger when adding publish, rollback, ISR, preview tokens, or a public renderer to a page builder.

  7. 07
    cms-tenancyv1.0.0

    Tenant a multi-tenant CMS by resolving a workspace you own — membership is yours (Clerk authenticates a person and nothing else), resolution never creates a workspace, the active-workspace cookie is a hint, and billing gates on the write. Trigger when adding workspaces, domains, seats, Stripe, or host routing to a page builder.

How it fits together

CMS page builder

Everything needed to take a visual page builder from an empty pair of apps to something people can design a real site in: the theme contract, the section catalog, the importer, pages-as-content, the customizer, the publish seam, and tenancy. Install the skills and let Claude work through them in order.

This is the architecture behind Spectre CMS (and the Mission Control storefront CMS it was ported from). The skills are the invariants, not the file paths — use them to build the same shape, or to extend one that already has it.

Order

  1. cms-theme-contract{ settings, templates, sectionGroups } as one JSON document of primitive setting values, normalized on every read, in a package that imports nothing. Section type is globally unique. Everything else is downstream of this.
  2. cms-section-catalog — one catalog the picker, the library, the sandbox and both renderers read: four artifacts per type (manifest, component, registry, preset), blocks vs CTAs, spacing generated from the manifest, and the checklist for adding one.
  3. cms-imported-sections — a customer design becomes DATA. The template is a tag-allowlisted tree, not an HTML string; types are namespaced custom:<id>; CSS is scoped on write; definitions snapshot at publish; archive, never delete. The field schema is the product, not the markup.
  4. cms-pages-and-templates — a page is a workspace-scoped row. The theme supplies only the layout. resolvePageVariant falls back to default, so a theme swap never 404s a live URL. Saving a page is publishing it.
  5. cms-customizer — the public renderer in an iframe, mounted off a query marker (a cookie will not arrive cross-origin), talking over a typed message union through data-section-id. Publishing the live theme is saving it.
  6. cms-publish-and-seam — the site never touches Postgres. The ISR cache key carries the host, because a shared key is one customer's site on another's domain. Versions are append-only; the snapshot is the bundle, not the pages.
  7. cms-tenancy — the workspace is yours; Clerk authenticates a person. Resolution never creates a workspace. The cookie is a hint. FREE is a sentinel. Past due keeps the site up. The webhook is the only writer of billing state, and entitlement gates the write.

Notes

Two invariants are worth stating before any code:

  • The public renderer and the customizer canvas are the same program. A second, editor-only render path will drift. Share the contract package, the registry, and every resolver.
  • A bundle that cannot be parsed is a site that silently stops rendering. That is why settings stay primitives, why normalizeBundle runs on every read forever, and why an imported section that cannot be vouched for loses one node rather than the import.

The pieces interlock more tightly than most bundles. The contract in step 1 is what step 5 mutates and what step 6 freezes. The catalog in step 2 is what step 3 must never shadow (custom:<id>). The page-as-row split in step 4 is why step 6's snapshot can omit pages without 404ing a live URL. The host-keyed cache in step 6 is the other half of the tenancy rules in step 7.

Steps 1, 2, 3 and 4 belong in a zero-dependency package both your API and your renderer import. That is what makes "the canvas is what publishes" true by construction rather than by maintenance.

Prefer to drop the files in?

Copies every skill in this recipe as one block — each section is headed with the path it belongs at (.claude/skills/<slug>/SKILL.md), so you can paste them straight into your project and reference them in your prompt.