Skip to main content
A brand in Substrate is a directory containing a YAML config. That file is the only place you author brand identity — the pipeline reads it and emits solver inputs, style descriptors, and baked per-mode token files for every platform. There is no JSON config, no root-level config file, and no registration step: brands are discovered by scanning the brands directory.
Demo brands only. All brand configurations bundled with the Substrate engine, and all brand names used in examples throughout these docs, are fictional demonstrations created to exercise the engine. They do not represent clients or customers of Substrate or its authors, and no affiliation with, sponsorship by, or endorsement from any real company is implied.

File layout

Two shapes are supported, and which one you get depends entirely on the directory structure.
A family directory holds a shared config.global.yaml, and each sub-brand directory holds a config.yaml containing only its deltas. Most of the engine’s bundled demo brands ship this way. For a fictional airline family, Aurora Airways:
Each sub-brand becomes its own brand, with the merged slug {parent}-{sub}aurora-rewards. See Inheritance for the merge rules.
Keys are authored in kebab-case and normalized to camelCase at load. heading-family becomes headingFamily, duration-base becomes durationBase, and so on throughout the schema. The exception is named-resource maps — intents, gradients, ramps, materials, rampOutputs — whose direct child keys are your own identifiers and round-trip verbatim one level deep. The scheme-track grammar keys (scheme-end, scheme-track, from-intent) also keep their kebab spelling at any depth, because the kernel consumes them that way. This rule holds for every config page in this section.

The required sections

Seven sections are non-optional. A config missing any of them is incomplete, and a config missing the required intents fails to load outright. Here is an example foundation for the fictional Acme brand (src/brands/acme/config.global.yaml), trimmed to the required sections:

Every section at a glance

Field-by-field detail, defaults, and a worked example for every section live in the Config Schema reference — each row links straight to it. The authoring guides in this section cover the why per domain.

What the loader does with it

Discovery is a directory scan. For each directory under the brands root, the loader checks for config.global.yaml (family) or config.yaml (flat), parses the YAML, normalizes the keys, deep-merges family and sub-brand layers where both exist, and stamps the slug from the directory structure. That slug is what appears in generated CSS as [data-brand="aurora-rewards"]. Two validations run at load. The intent roster must contain brand and neutral, or the load throws a missing-required-intent error. Any intent using scheme-track grammar is desugared and validated, including reference cycles and unknown intent names.
Substrate has no build verb and no per-brand build flag. Generation runs as npm run generate in the engine checkout, which regenerates every brand. The substrate CLI is a consumer-side onboarding tool with the verbs init, add, upgrade, adopt, setup, and artifact — see the CLI reference.

Explore each config section

Color

The open intents map, hue and chroma, scheme tracks, and the solver primitives each intent produces.

Typography

Base size, scale ratio, and the continuous power-of-ratio model that replaces a step ladder.

Spacing & Density

One space unit, multiplied at each use site by the runtime density and scale factors.

Motion

Base duration and easing, and how the motion factor scales them at runtime.

Core Concepts

Why the values are computed at runtime rather than baked, and how the preference vector drives them.

Multi-Brand

Families, sub-brands, deltas-only authoring, and the per-brand output tree.