The intents map
Colors live under a top-levelintents: key. It is an open map: only brand and neutral are required, and everything else is the brand’s own decision. Each intent carries two OKLCH parameters:
hue— the color angle, 0 to 360chroma— colorfulness, 0 (achromatic) to roughly 0.4, with the real ceiling depending on hue and display gamut
src/brands/acme/config.global.yaml):
Intent names are your identifiers, so they round-trip verbatim — the kebab-case-to-camelCase normalization described in the config overview does not touch the direct children of
intents. acme-cyan stays acme-cyan.Conventional names, not reserved ones
There is no built-in-versus-custom split in Substrate. The pipeline reads whatever intents a brand declares; it never consults a fixed list to decide what a brand “should” have. Custom intents produce the same solver primitives, the same baked per-mode tokens, and the same platform output as any other. What does exist is a convention. Across the engine’s bundled demo brands, these names recur and carry consistent meaning:
Brands lean on custom intents heavily. A fictional Aurora Airways family might declare
aurora-navy and aurora-burgundy at the family level, while its Rewards sub-brand adds loyalty-tier intents like tier-gold, tier-silver, tier-platinum, and tier-accent — each getting the full solver treatment.
Intents that vary across the scheme axis
An intent’s hue and chroma can be a function of the scheme position rather than a constant — useful when a color’s perceived character drifts at luminance extremes and you want to compensate. The grammar has two tiers.- scheme-end
- scheme-track
The shorthand: give a second hue and chroma for the dark end. It desugars to stops at positions 0 and 1.
Lightness: the one exception
Lightness never appears in an intent, at any tier. The solver owns it at every scheme position, which is what lets a single declaration produce correct foregrounds on light surfaces, dark surfaces, and every point between. The exception is gradients. A gradient stop references an intent by name and does pin a lightness, because a gradient is a designed artifact rather than a contrast-solved one:What each intent produces
For every declared intent, the runtime writes a set of--ucs-* solver primitives that downstream CSS composes with calc():
--ucs-{intent}-hueand--ucs-{intent}-chroma— the evaluated character at the current scheme position, after the warmth and CVD user-compensation transforms--ucs-{intent}-fg-l,--ucs-{intent}-border-l,--ucs-{intent}-surface-l— solved lightness for each channel against the actual surface--ucs-{intent}-pattern— the non-color redundancy channel
@property so they animate and inherit predictably. The shape of a brand’s generated css/brand.gen.css, here for a custom aurora-navy intent:
generated/brands/<brand>/<sub-brand>/css/tokens.light.gen.css:
[data-brand="aurora-rewards"] for brand scoping and [data-mode~="brand"] for intent selection on an element carrying data-ucs. Substrate emits no theme attribute and no per-color variable family of its own — the --ucs-* primitives above are the whole vocabulary.
APCA Contrast Solving
How the solver searches the lightness axis, and the fixed Lc policy it targets.
Brand Config Overview
The rest of the schema: elevation, typography, shape, motion, space, flexibility, and presets.