> ## Documentation Index
> Fetch the complete documentation index at: https://substrate.docs.unknowncreatives.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Brand Config Schema Reference

> The full YAML brand config schema: intents, elevation, typography, shape, motion, space, flexibility, and presets — illustrated with an example brand config.

Brand configuration is **YAML**, discovered by directory convention — there is no `substrate.config.json` and no registration file:

| File                                     | Role                                               |
| ---------------------------------------- | -------------------------------------------------- |
| `src/brands/<name>/config.yaml`          | A flat, standalone brand (e.g. `acme`).            |
| `src/brands/<family>/config.global.yaml` | The shared base of a brand family (e.g. `aurora`). |
| `src/brands/<family>/<sub>/config.yaml`  | A sub-brand's **deltas** over the family base.     |

A separate `substrate.config.yaml` exists in client projects, but it is the org-wide *system overlay* scaffolded by `substrate init` — not a brand config. Keys are authored **kebab-case** and normalized to **camelCase** at load (`heading-family` → `headingFamily`); child keys of named-resource maps (`intents`, `gradients`, `materials`, `ramps`) keep their spelling. Inheritance is a recursive deep merge — see [Inheritance](/multi-brand/inheritance).

<Note>
  **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.
</Note>

## Complete example

An example brand config (fictional brand), trimmed — `src/brands/acme/config.global.yaml`:

```yaml theme={null}
intents:
  brand:
    hue: 277
    chroma: 0.268
  neutral:
    hue: 251
    chroma: 0.012
  danger:
    hue: 11
    chroma: 0.228
  acme-cyan:            # custom intents are first-class
    hue: 228
    chroma: 0.147

elevation:
  step-light: 0.015
  step-dark: 0.04
  sunken-step: 0.02

typography:
  heading-family: "'Acme Sans', 'Inter', system-ui, sans-serif"
  body-family: "'Acme Sans', 'Inter', system-ui, sans-serif"
  base-font-size: 1     # rem
  scale-ratio: 1.25
  fluid:
    rate: 0.5
  density:
    font-size-k: 0
    font-weight-k: 0

shape:
  radius-base: 6        # px

motion:
  duration-base: 200    # ms
  easing: "cubic-bezier(0.25, 0.1, 0.25, 1)"

space:
  unit: 4               # px

flexibility:
  scheme: true
  contrast:
    min: 0.75
    max: 1.5
  density:
    min: 0.8
    max: 1.3
  type-scale:
    min: 0.9
    max: 1.4
  motion:
    min: 0
    max: 1

presets:
  mode:
    light:
      scheme: 0.02
      contrast-factor: 1.0
    dark:
      scheme: 0.74
      contrast-factor: 1.0
```

## Sections

### `name` and slug

`name` is the display name (`name: Aurora Airways - Rewards`). The **slug is not authored** — it is derived from the directory name by the loader (`aurora/rewards` → `aurora-rewards`) and becomes the runtime's `data-brand` value.

### `intents` — required

An **open map** of intent colors. Only `brand` and `neutral` are hard-required (the loader throws `missing-required-intent` otherwise). The conventional names — `brand`, `neutral`, `danger`, `warning`, `success`, `info`, `beta` — get no special treatment over custom intents; the brand's own map is the source of truth. Each intent authors `hue` (0–360) and `chroma` (0–0.4) only — lightness is solver-owned.

An intent's color may vary across the scheme axis with a **scheme track**: `scheme-end: { hue, chroma }` (shorthand for the dark end), or a full `scheme-track` list of stops `{ at, hue, chroma }` / `{ at, from-intent }` with optional `blend: oklch | oklab`. Schema-valid, though none of the bundled demo brands currently exercises it.

### `elevation` — required

`step-light`, `step-dark`, and `sunken-step` — the per-level lightness offsets for elevated and sunken surfaces in each scheme direction. A typical shape keeps light-mode elevation subtle (`0.015`) and lets dark mode carry more (`0.04`).

### `typography` — required

`heading-family`, `body-family`, optional `mono-family`; `base-font-size` (**rem**, not px); `scale-ratio` (the modular ratio — there is no engine default; the demo brands range from 1.2 to 1.25); optional `fluid` (`rate`, `floor`, `ceiling`, `quantum`) and `density` coefficients (`font-size-k`, `font-weight-k`). See [Type Tokens](/reference/type-tokens) for how these compose.

### `shape`, `motion`, `space` — required

`shape.radius-base` (px) is the base for the runtime-scaled `--radius`, so corner rounding tracks the density slider; the bundled demo brands range from `4` to `12`. `motion.duration-base` (ms) **and** `motion.easing` are both required — values vary by brand character, from `75` with `linear` for a transactional surface to `200` with a custom cubic-bezier for a more polished one. `space.unit` (px, a bare number) is the single spatial unit everything composes from; the bundled demo brands all use `4`.

### `flexibility` — required

The brand's policy for how far users may push each preference axis: `scheme` (boolean), and `{ min, max }` bounds for `contrast`, `density`, `type-scale`, and `motion` (optional `effects`). These bound the runtime preference vector, whose engine defaults are:

| Axis              | Default                        |
| ----------------- | ------------------------------ |
| `scheme`          | `0`                            |
| `contrastFactor`  | `1`                            |
| `densityFactor`   | `1`                            |
| `typeScaleFactor` | `1`                            |
| `motionFactor`    | `0.75`                         |
| `warmth`          | `0`                            |
| `cvd`             | `{"type":"none","severity":0}` |

### `presets` — optional

Named positions on the preference continuum: `mode`, `density`, and `contrast` are each an **open map** of brand-named levels. Mode levels carry `scheme` and `contrast-factor` — note brands choose *positions*, not endpoints (a light level may sit at `scheme: 0.02`; a dark level at `0.74`, a deep navy rather than black). The engine also ships brand-independent presets:

| Preset             | scheme | contrastFactor |
| ------------------ | ------ | -------------- |
| `light`            | 0      | 1              |
| `dark`             | 1      | 1              |
| `dimmed`           | 0.65   | 0.95           |
| `highContrast`     | 0      | 1.3            |
| `darkHighContrast` | 1      | 1.3            |

A fuller example for the fictional Acme brand — five mode levels and a density map:

```yaml theme={null}
presets:
  mode:
    light:
      scheme: 0.02        # not pure white
      contrast-factor: 1.0
    dark:
      scheme: 0.74        # brand-dark surface — never pure black
      contrast-factor: 1.0
    dimmed:
      scheme: 0.55
      contrast-factor: 0.95
    high-contrast:
      scheme: 0.02
      contrast-factor: 1.3
    dark-high-contrast:
      scheme: 0.78
      contrast-factor: 1.3
  density:
    compact:
      density-factor: 0.85
    default:
      density-factor: 1.0
    comfortable:
      density-factor: 1.15
```

A brand that omits `presets.mode` does not get those five modes — they are authored, not automatic.

### `ramps` and `color` — optional

`ramps` is an open map of ramp recipes — the only place a `--color-*` style output exists; a brand that opts in gets static stepped scales generated from an intent. Each recipe: `intent` (source intent name), `steps` (number list, lightest → darkest; pure labels — lightness is assigned by position), `lightest-l` (default `0.97`), `darkest-l` (default `0.18`), `gamut` (`srgb` | `p3`, default `srgb`), `outputs` (adapter list, default `[tailwind]`), `sample`.

```yaml theme={null}
ramps:
  brand-scale:
    intent: brand
    steps: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]
    lightest-l: 0.97
    darkest-l: 0.18
    gamut: srgb
```

The sibling top-level `color` key is ramp *orchestration*, not brand color: `color.ramps` holds `defaults` and per-intent settings (`stops`, `lightest-l`, `darkest-l`, `alpha-equivalent`, `surfaces`, `sample`), and `color.ramp-outputs` maps each platform (`web`, `ios`, `android`, `react-native`) to `formats`, `materialization` (`dynamic` | `static` | `both`), and an `include` list.

### `gradients` — optional

An open map of named gradient recipes — the one place lightness is authored. Each: `type` (`linear` | `radial` | `conic`), geometry (`angle` for linear/conic, `shape` and `origin` for radial, optional `repeat`), and two or more `stops`. A stop references an `intent` by name (or `transparent`) and carries `at` (position) plus either an authored `lightness` or a `channel` (`surface` | `fg` | `border`) to use the APCA-solved value, with optional `chroma` and `alpha`:

```yaml theme={null}
gradients:
  hero:
    type: linear
    angle: 135
    stops:
      - intent: brand
        lightness: 0.52
        at: 0
      - intent: acme-mint
        lightness: 0.91
        at: 100
```

### `materials` and `effects` — optional

`materials` is an open map of named surface treatments applied via the `data-mode` token list. Each material declares channel blocks — `background`, `foreground`, `border` — and each channel takes a `filter` composition (`blur`, `saturate`, `brightness`, `contrast`, `grayscale`, `hue-rotate`, `invert`) plus channel properties (`alpha`, `noise`, `lightness`, `chroma`):

```yaml theme={null}
materials:
  frosted:
    background:
      filter: { blur: 2, saturate: 1.8 }
      alpha: 0.7
      noise: 0.3
    foreground:
      filter: { brightness: 1.05 }

effects:
  blur-unit: 4    # px — blur values are multiples of this
```

`effects` holds `blur-unit` (px) — filter blur values are emitted as multiples of it (`blur(calc(var(--blur-unit) * 2))`). Backdrop filters are wrapped in an `@supports` guard, and a non-zero `noise` adds an SVG-turbulence grain overlay.

### `system` — optional

A free-form map deep-merged over the engine's `system.config.yaml` defaults for this brand only (cascade: defaults → user override → brand block). Overridable keys include optical typography rates and clamps, CVD safe arcs, and layout structure thresholds:

```yaml theme={null}
system:
  typography:
    optical:
      line-height-rate: 0.14
```

<Warning>
  A config missing `intents.brand`, `intents.neutral`, or any required section fails at load — the audits found the previous version of this page documented a schema under which **no config could ever validate**. Every field on this page is checked against the ground-truth manifest in CI.
</Warning>
