For the setup steps that get Substrate into your repo in the first place — vendoring the engine, running
substrate init, wiring bundler configs — see the Quickstart.
The three public aliases
Client code imports Substrate through exactly three aliases. This is the entire import contract:The engine-swap guarantee. These three specifiers are the supported client API, and swapping the engine underneath never changes your import statements. A verified sealed-engine setup resolves
@substrate/engine to a prebuilt runtime bundle rather than to source, using the same specifier — so the application never holds a path into engine internals.substrate init writes the aliases into tsconfig.json automatically and prints the block for bundler and test configs, which it won’t edit because they’re arbitrary code. Re-print them any time with substrate init --report-aliases.
What is not a client path
Two patterns look plausible and are not supported:@substrate/engine/*(with the wildcard) reaches deep engine subpaths. It exists as an escape hatch for Substrate’s own demo, is classified internal, and must not be copied into client code.- Relative paths into engine internals, such as
../../../kernel/color/surface, couple your code to one engine layout. This is exactly the dependency the Surface component was refactored to remove.
@substrate/engine, keep ./ imports to your own colocated files, and use nothing else to leave your directory. If a seam you need is missing from the barrel, the fix is to add it to the barrel rather than to route around it.
The runtime call sequence
Substrate requires a JavaScript runtime on the web — but on the supervisedsubstrate setup path you don’t write this wiring at all: setup --apply generates src/substrate.setup.ts (the CSS import plus initializeSubstrate()) and wires it into your application entry itself. On the vendored path, the same module is one import and one call:
initializeSubstrate() defaults to the first registry brand and defaultPreferences(), accepts { brand, preferences } overrides, and is exactly equivalent to the lower-level sequence:
syncBrandToCssVars(brand) sets data-brand="<slug>" on the root element and writes the brand’s static custom properties — typography families and fluid sizing, shape, motion durations, spacing units, effects. Call it once per brand, and again only if the brand changes.
syncPrefsToCssVars(prefs) writes the raw preference axes to the root: --scheme, --contrast-factor, --density, --type-scale-factor, --motion-factor, and --cvd-achromat. It also toggles the data-cvd-achromat attribute, which is what activates the pattern overlays. Everything the generated CSS composes with calc() reacts to this call alone.
updateAllVars(brand, prefs) does the expensive work: it derives the base and elevated surfaces, runs the warmth → cvd → apca pipeline, and writes the solved per-intent --ucs-* primitives to :root.
On any preference change, repeat the last two:
:root, elements consume them by opting in — that contract is small enough to hold in your head and lives on its own page: Markup Opt-In.
Following OS preferences
The runtime ships helpers that read OS media queries into the vector and keep listening. Each returns a cleanup function that detaches its listeners:prefers-color-scheme and prefers-contrast media queries on its own — a server-rendered page paints accessibly, in the user’s OS scheme, before a single byte of JS executes (see the no-JS floor). And initializeSubstrate() covers the entire startup sequence. The helpers exist to keep the live preference vector following OS signals after startup.
prefers-color-scheme is deliberately not among the helpers — scheme is a continuous axis and often an in-app choice, so you decide whether the OS drives it. See Light & Dark.
Markup Opt-In
Required next step: data-ucs and data-mode, the two attributes every consuming element carries.
Surface Component
Required when surfaces nest: re-solving contrast against a local background.
Web
The generated CSS layers, the custom property namespaces, and the calc-composed model.
Modes Overview
The preference vector the runtime call sequence is driving.