Token Contract
The canonical CSS-variable contract every brand instance must satisfy — 67 tokens, a date-based contract version, the exact-match rule, and pnpm brand:lint.
Token Contract
The contract is the canonical set of CSS-variable names and structure that every brand profile must define. An instance is one brand's values for exactly those names. This is the concrete form of "full controllable palette" — the brand owns every default-driving variable, so per-tenant recoloring is a values-only swap (ADR 0002 §3).
Source of truth
lib/brand-contract-tokens.json is the single canonical manifest (ADR 0002 §3, O-5 decision 2026-06-04). It declares:
contractVersion— a date-based version (currently2026.06.04).tokens— the exhaustive list of required CSS custom-property names (currently 67 tokens).
lib/brand-contract.ts is the typed Zod surface over that manifest. It exports CONTRACT_VERSION, REQUIRED_TOKENS, and BrandTokenProfileSchema. The schema validates a brand profile shaped as:
{
namespace: string, // ^[a-z0-9][a-z0-9-]*$
contractVersion: "2026.06.04",
tokens: Record<string, string>
}The match is exact. BrandTokenProfileSchema fails if a profile is
missing any contract token or introduces an off-contract token. A
brand instance defines every contract variable and no extras.
The 67 tokens
The contract spans these groups (see lib/brand-contract-tokens.json for the authoritative list):
| Group | Tokens |
|---|---|
| Brand accents | --brand-cyan, --brand-cyan-light, --brand-blue, --brand-ice, --sand, --sand-light, --sand-whisper |
| Surface ladder | --background, --card, --popover, --muted, --obsidian, --surface-1, --surface-2, --surface-3 |
| Semantic | --foreground, --primary, --secondary, --accent, --destructive, --border, --input, --ring, plus their -foreground pairs |
| Muted text | --muted-foreground, --muted-fg-low, --muted-fg-vlow, --title-foreground |
| Charts | --chart-1 … --chart-5 |
| Gauges | --gauge-healthy, --gauge-attn, --gauge-warn, --gauge-critical |
| Heatmap | --heatmap-1 … --heatmap-4 |
| Rank ramp | --rank-1 … --rank-6 |
| Sidebar | --sidebar, --sidebar-foreground, --sidebar-primary, --sidebar-accent, --sidebar-border, --sidebar-ring, plus -foreground pairs |
| Shadows | --shadow-card, --shadow-signal, --shadow-cta-glow, --shadow-sheet |
| Typography & radius | --font-sans, --font-mono, --font-display, --radius |
Every value is oklch (or a color-mix() / shadow composite built on oklch tokens). Canvas / WebGL effects are the only exception — they may use literal color constants where CSS variables are unavailable, and those literals must mirror the brand-palette values.
Validation — pnpm brand:lint
pnpm brand:lintbrand:lint (scripts/brand-contract-check.mjs) enforces the contract against each registries/{ns}/brand-system.css: every :root block is parsed, declarations are unioned, and the resulting set must match the manifest exactly — a build fails if a tenant omits a contract token or introduces an off-contract one (registries/README.md).
The script reads lib/brand-contract-tokens.json directly in plain Node and
encodes the same exact-match rule as BrandTokenProfileSchema. The two must
stay in sync — if the contract semantics ever change, change both
(lib/brand-contract.ts header note).
Versioning
The contract version is date-based (2026.06.04), matching the brand-package versioning scheme (see Versions & Snapshots). A brand profile pins the contractVersion it was authored against, so a contract change is an explicit, reviewable event rather than silent drift. A generated requirements.schema.json is derived from this manifest and shipped in each version snapshot.
Token meanings
For the full annotated token reference — every value, its purpose, and the brand rationale — see the Vandoko Dark Theme doc. Vandoko's instance of this contract ships as the vandoko-brand-system registry:base (with vandoko-theme as its alias); both carry the same CSS. See Consuming the Brand System for installing it.
Brand System
How the Vandoko Registry distributes and governs brand systems — the oklch token contract, ownership boundaries, and the multi-tenant model.
Ownership Boundary
What the Registry owns (schema + default UI), what the Portal owns (tenant runtime data), and what the Tenant owns (their brand profile values).