The schema/ module implements the settings type system that themes
declare in theme.json and in node {% schema %} blocks. It has three
layers: the type taxonomy (SettingDefinition), validation and
defaults (load-time and render-time), and the color cascade
(palette → scheme → CSS variables).
The taxonomy
SettingDefinition is a tagged enum, the JSON type field selects the
variant (#[serde(tag = "type", rename_all = "snake_case")]). Each variant
wraps a config struct in schema/types.rs. The 36 types group into:
| Group | Types |
|---|---|
| Basic inputs (8) | checkbox, number, radio, range, select, size, text, textarea |
| Resource pickers (8) | product, product_list, collection, collection_list, page, article, article_list, blog |
| Color (5) | color, color_background, color_palette, color_scheme, color_scheme_group |
| Text/media (11) | font_picker, html, image_picker, inline_richtext, link_list, code, metaobject, metaobject_list, richtext, text_alignment, url, video, video_url |
| Layout (3) | header, paragraph, group |
Every config struct carries id + label (+ optional info); the full
field list per type is in Schema blocks → Setting types.
The editor’s SettingsFormRenderer maps each type to a form control
(apps/storeadmin, documented in
brainstorms/13.h-theme-editor-architecture.md).
SettingType is the discriminant-only view (SettingDefinition::kind()),
used by value validation.
Validation
Three operations, at three times:
Load-time schema validation, validate()
Runs on every setting at engine startup (manifest settings and every node schema). A failing schema fails engine startup, never render time:
range:min < max, anddefaultwithin[min, max].select/radio: ≥ 1 option;defaultmust be an option value.color_palette: 2–20 colors; keys must start with a letter.font_picker:defaultrequired.metaobject/metaobject_list:metaobject_typerequired.size: non-emptydefaultmust be a valid CSS length;unitmust be a supported unit.
Default resolution, default_value()
checkbox→falsewhen unset;select/radio→ first option;range→ its requireddefault;size→ its stringdefault;color_palette→ the palette object.- Resource pickers,
url,video_url→None(merchant must choose). header/paragraph/group→None(no value output).
Render-time merge, apply_setting_defaults()
Before a node renders, the merchant’s stored settings map is merged with
the schema: missing keys get defaults; stored values that fail
validate_value() are salvaged by coerce_value() where possible and
otherwise fall back to the default. Structural group entries are
flattened depth-first before merging (they contribute no keys), so grouped
settings behave exactly like top-level ones. Value rules:
checkboxvalues must be boolean;number/rangenumeric.codecapped at 50 KB;*_listandmetaobject_listcapped at 50 entries.sizevalues must be valid CSS lengths (see Schema blocks → Setting types). A legacy bare number (stored by the old pxrangesettings) is re-emitted with the setting’sunit(pxdefault), so pre-existing merchant data keeps rendering; anything else falls back to the default.
The color cascade
Colors flow through four layers (schema/color.rs):
flowchart LR P[color_palette<br/>named colors] --> S[color_scheme_group<br/>semantic roles] S --> C[scheme picker<br/>per-section color_scheme setting] C --> V[generate_scheme_css<br/>--color-* variables]
color_palette(theme-level), named colors like"primary": "#0f0f0f".ColorPalette::from_configparses hex (#RGB,#RRGGBB,#RRGGBBAA) intoCssColor.color_scheme_group(theme-level), maps semantic roles (text,background,links,icons,primary_button, …) to palette references ("{{ settings.colors.primary }}") or literal hex.ColorSchemeGroup::resolveexpands references into concrete colors and buildsscheme_1(an explicitdefault_schemeoverrides role defaults;BackgroundValuesupports solid or gradient backgrounds). Additional named schemes fromsettings_data.jsonare documented in the spec but not loaded by the renderer, multi-scheme resolution is scoped to the API binary today.- Scheme picker (section-level), a node’s
color_schemesetting selects which scheme applies to the section. - CSS generation,
generate_scheme_cssemits a:rootblock and a.color-scheme-{id}scoped block of--color-{role}custom properties.engine/page.rs::build_css_variablescallsasset::generate_css_variableswith the active scheme and injects the result into the page<style>.
The default theme doesn’t use named schemes, its global colors palette
is surfaced directly as {{ settings.colors.* }} and its node
color_scheme selects map to CSS modifier classes instead, but the
mechanism is fully implemented and exercised by the test fixture theme.