§ Atlas docs · Manifest reference

The schema is the contract.

Six fields at the top. Seven on every block. Read this once and you have the whole surface.

The shape

A manifest is a JSON object the compiler emits and the plugin reads. The top level is AtlasFormat; the leaves are BlockNodes nested through innerBlocks. There is no second representation, no intermediate cache, no editor state to reconcile.

manifest.json JSON
{
  "meta": {
    "name": "home",
    "version": "0.4.2",
    "platform": "wordpress",
    "wpVersion": "6.4",
    "atlasVersion": "0.4.2"
  },
  "tokens": { /* colors, typography, spacing, radii, shadows */ },
  "layout": {
    "templates": {
      "home": { "blocks": [ /* BlockNode[] */ ] }
    }
  },
  "motion": { "presets": { ... }, "easings": { ... } },
  "validation": { "rules": [ /* ValidationRule[] */ ] }
}

Top-level fields

meta
Identity for the manifest itself. name, version, platform: "wordpress", wpVersion, atlasVersion. The plugin uses atlasVersion to decide whether it knows how to render this manifest.
tokens
Design tokens scoped to this manifest — colors, typography, spacing, radii, shadows. Resolved into CSS custom properties at render time and emitted on the block's root element. Keeps a manifest portable across themes.
layout
The block tree. templates is required; parts and patterns are optional. Each is a record of named { blocks: BlockNode[] } collections — the atomic shape every renderer walks.
motion
Animation presets and easings shared across the manifest. Presets pick from a fixed set (fade-in, slide-up, zoom-in, …); easings pick from a fixed set (ease-out, spring, snap, …). Components reference them by name; the runtime resolves them through GSAP.
validation
A list of rules the compiler runs against the block tree before emitting the manifest. Built-in rule IDs include heading-hierarchy, image-alt-required, color-contrast, max-nesting-depth. A failing rule with severity error aborts the build.
runtime
Optional. Per-manifest runtime hooks — extra css, extra js, and a gsap opt-in flag. Most manifests leave this off. Use it when a block needs CSS or JS that is not expressible as tokens or motion presets.

BlockNode

Every entry in layout.templates[*].blocks is a BlockNode. The shape mirrors WordPress\'s native block JSON — Atlas wraps it, it does not replace it. That is why one block inside Atlas can compose with any block WordPress ships natively.

type
The block type — a namespaced string like core/heading, core/paragraph, or atlas/hero. The plugin looks up the renderer by this key. Unknown types render their innerHTML verbatim and emit a console warning.
attrs
The block's typed attributes — the data the renderer needs to produce HTML. Shape varies by type; the compiler validates each block's attrs against the schema registered for that type.
innerHTML
The HTML the compiler emitted for this node, captured at compile time. The renderer uses it as the canonical output and as the graceful-degradation fallback when something downstream fails.
innerBlocks
Child BlockNodes. The same shape applies at every depth. Composition is recursion — there is no separate container or wrapper type.
innerContent
Optional. Interleaving array of strings and null placeholders that mirrors WordPress's native block JSON. null marks a slot where an entry from innerBlocks is rendered. Only set when this block came from a WordPress source post.
rawCSS
Optional. Block-scoped CSS, compiled and hashed. Emitted inline alongside the rendered HTML. Use sparingly — most styling should come from tokens.
metadata.bindings
Optional. Maps an attribute key to a data source ({ source: "post_meta", args: { key: "..." } }). At render time the plugin resolves the binding against WordPress and substitutes the result into attrs.

Block tree

A page is a tree of BlockNodes. Composition is recursion through innerBlocks — there is no separate group or section primitive. The compiler walks the tree, runs every validation.rules check at every depth, and emits the final manifest only if every error-severity rule passes.

manifest.json — layout.templates.home.blocks JSON
[
  {
    "type": "atlas/hero",
    "attrs": { "heading": "Page builders hold you back." },
    "innerHTML": "<section class=\"hero\">...</section>",
    "innerBlocks": []
  },
  {
    "type": "core/group",
    "attrs": {},
    "innerHTML": "<div class=\"wp-block-group\">...</div>",
    "innerBlocks": [
      { "type": "core/heading", "attrs": { "level": 2 }, "innerHTML": "…", "innerBlocks": [] }
    ]
  }
]

Islands & motion

A node opts into client-side hydration through an atlas:island entry on its attrs — values mirror Astro\'s directive vocabulary ("client:load", "client:visible", "client:idle"). The renderer emits a data-island attribute on the rendered element and queues the JS bundle for the matching directive. Nodes without an island entry ship zero JavaScript.

Motion is declared the same way — an atlas:motion entry on attrs names a preset from motion.presets. The runtime resolves it through GSAP at hydration time. Reduced-motion preferences pin the end state automatically.

Graceful degradation

Every BlockNode carries its rendered HTML in innerHTML. When the plugin cannot perform a live render — component removed, schema drift, plugin deactivated — the editor falls back to that snapshot. Deactivation degrades to plain HTML, not to shortcode debris. The page stays readable while you decide what to do next.

Failure modes

Three things can go wrong with a manifest. All three have deterministic outcomes.

  • Unknown atlasVersion in meta. The plugin refuses to render the manifest and emits a console warning. Upgrade the plugin or recompile the manifest against the installed version.
  • Unknown block type. The renderer emits the block\'s innerHTML verbatim and logs a warning. Common cause: a block was renamed in a package without recompiling the manifests that reference it.
  • A validation.rules error fires at compile time. The build aborts before the manifest is emitted — the broken page never reaches production. Warning-severity rules log but do not block.

There is no fourth case. If a page renders blank, the cause is in troubleshooting — not in the manifest schema.