Skip to content

theme.json

The theme manifest, global settings, page presets, and section groups.

Updated View as Markdown

theme.json is the theme’s manifest. It is parsed into a ThemeManifest (crates/ringroad-renderer/src/theme/manifest.rs) at engine startup. Node schemas are not declared here, they live in each node file’s {% schema %} block, but the manifest carries everything else:

{
  "name": "Default",
  "version": "1.0.0",
  "settings": [  ],
  "presets": {  },
  "section_groups": {  }
}
Key Type Purpose
name string Human-readable theme name.
version string Semver-ish theme version.
settings array Global theme settings schema (colors, fonts, layout). Drives the editor’s global settings panel and {{ settings.* }} in templates.
presets object Default node trees per page key, what a fresh page looks like before the merchant edits it.
section_groups object Named containers rendered on every page (header, footer), each with its own preset.

settings, global settings schema

Each entry is a setting definition, the same JSON shape used inside node {% schema %} blocks. The type field picks the control and its config struct. The default theme declares 14 settings (plus 3 header dividers):

{
  "type": "color_palette",
  "id": "colors",
  "label": "Colors",
  "default": {
    "primary": "#0f0f0f",
    "primary_content": "#ffffff",
    "secondary": "#8b6f4e",
    "accent": "#3a4a3a",
    "base_100": "#fafaf9",
    "base_200": "#f2f0ed",
    "base_300": "#6b6360",
    "base_content": "#171717",
    "success": "#3b5e3b",
    "warning": "#b07d2e",
    "error": "#9b2c2c"
  }
}

The colors palette is special: its keys become {{ settings.colors.* }} in templates and drive the design tokens the layout emits. The other global settings in the default theme:

id type Default Notes
heading_font select old_standard Also inter / georgia / helvetica / system
body_font select inter Same option set
font_size range 16 px (14–20) Base font size
heading_weight select 400 300–600
container_width select 4xl xs4xl container scale (20–56rem)
section_spacing range 5 rem (2–10) Vertical padding per section
container_gutter range 1.5 rem (0.5–4) Horizontal page padding
corner_radius select sharp sharp / moderate / rounded / pill
social_instagramsocial_tiktok url , Footer social links
html_head textarea "" Raw HTML injected into <head>

The select/range/url/textarea/header types are all documented in Setting types.

presets, default page trees

A preset is a complete PageConfig: node instances keyed by stable IDs plus an order array. When a page has no saved merchant config, the engine falls back to theme.presets[page_key].

"presets": {
  "index": {
    "order": ["hero-1", "ft-1", "cols-1", "testim-1", "nl-1"],
    "sections": {
      "hero-1": { "type": "hero", "settings": {}, "children": {}, "child_order": [] },
      "ft-1": {
        "type": "featured_products",
        "settings": {},
        "children": {
          "ft-img":   { "type": "product_image", "settings": {} },
          "ft-title": { "type": "product_title", "settings": {} },
          "ft-price": { "type": "product_price", "settings": { "show_compare": true } }
        },
        "child_order": ["ft-img", "ft-title", "ft-price"]
      }
    }
  }
}

The default theme defines presets for index, product, contact, catalog, collection, search, and collections. cart and checkout have empty presets (order: []) because those pages are hand-written templates, not node compositions.

Pre-filled children

A preset can instantiate entire child trees with settings, note that the merchant’s page config replaces the whole preset when saved, so presets are only the starting point. featured_products’s children (product_imageproduct_titleproduct_price) demonstrate the “atomic product card” pattern: a section node renders each child once per product (see Children strategies).

section_groups, the shared chrome

Section groups are named node containers rendered on every page. The renderer keys them by convention: @root:header-group and @root:footer-group (constants in src/keys.rs). Each group has a preset exactly like a page:

"section_groups": {
  "@root:header-group": {
    "name": "Header",
    "max_sections": 3,
    "preset": {
      "order": ["announce-1", "navbar-1"],
      "sections": {
        "announce-1": { "type": "announcement_bar", "settings": {}, "children": {}, "child_order": [] },
        "navbar-1":   { "type": "navbar", "settings": {}, "children": {  }, "child_order": [  ] }
      }
    }
  },
  "@root:footer-group": {
    "name": "Footer",
    "max_sections": 3,
    "preset": {  }
  }
}

max_sections caps how many top-level nodes the editor lets a merchant place in the group. allowed_sections (an optional allow-list of node types) is supported by the schema but unused by the default theme.

Validation at load

The engine validates the manifest when the theme loads: every setting in settings and in every node’s {% schema %} must pass SettingDefinition::validate() (for example a range default must sit between min and max, a select default must be one of its options). A bad schema fails engine startup, never render time. See Settings validation for the full rule list.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close