Brik Design System
Foundation

Token Anatomy

The seven disambiguated concepts that describe every BDS token — Anatomy, ID, Tier, Library, Layer, Mode, Tenet — and the four-tier abstraction stack every token belongs to.

Every BDS token answers seven independent questions. Without distinct vocabulary for each, agents and humans invent parallel taxonomies and the system drifts (portal #512 / #553, retired --theme-brik-*, retired --brand-{tier}). This page locks the vocabulary so the discipline lives in the words themselves.

Seven concepts, seven words

ConceptTermQuestion it answersValues
Naming structureAnatomyHow is it named?--{id}-{body}
System identifierIDWhich system owns this decision?a handle from the ID registry: bds · vale-partners · …
Abstraction levelTierWhat kind of token is it?Raw · Primitive · Semantic · Component
Source of truthLibraryWhere is it defined?Foundations Library · [Client] Brand Kit Library
CSS cascade originLayerWhich @layer carries its value?bds-tokens · bds-components · client-theme · client-overrides
Value axisModeWhich axis varies its value?color light/dark · borderwidth thin/bold · spacing compact/spacious · …
System pillarTenetWhich system pillar governs it?Foundation · Theming · Motion · Content

These are orthogonal. A token has one answer for each. Mixing them — calling --brand-tertiary a "tier" because it sits at the top level — is the failure mode the cleanup retired.

Before referencing a token name, you should be able to answer all seven. If you can't, you're guessing.

ID and the token shape

Every token name has one shape, declared once in tokens/naming-grammar.json (ADR-043):

--{id}-{body}

The ID is always the first segment. It names the system that owns the decision. The body is what the Tier defines (below). ID is the term, not "Layer": Layer already names the CSS @layer that carries a value, and one word for two meanings is the drift this page exists to stop.

Today, only the Component tier carries an ID. Three Component names are declared in dist/tokens.css (--bds-slider-*); the rest are declared in each component's CSS (--bds-toast-shadow in Toast.css). The ID rename for Primitive and Semantic names ships in #2670. Until it does, those names are the ID-less body: --background-brand-primary, --color-poppy-500. Reference only names that exist in dist/tokens.css. A name with the ID in front of a Primitive or Semantic body is the target, not a usable name yet.

TierBodyIn dist/tokens.css todayAfter #2670
Primitive (colour)color-{family}-{step}--color-poppy-500--bds-color-poppy-500
Primitive (scale){scale}-{step}--space-400--bds-space-400
Semantic{purpose}-{role}[-{state}]--background-brand-primary--bds-background-brand-primary
Component{component}-{property}--bds-slider-thumb-size--bds-slider-thumb-size

The ID registry

An ID is a registered handle for the system (repo) that owns the decision. It is not derived from the repo slug. The registry is the ids.registry object in the grammar file, which is the source of truth; this table is a copy and the file wins.

IDSystem (repo)Library
bdsbrik-bdsFoundations + the default (Brik) Brand Kit that ships in dist/tokens.css
vale-partnersweb/vale-partnersVale Partners Brand Kit
tncldweb/tncldTNCLD Brand Kit
birdwellweb/birdwell-mutlakBirdwell Brand Kit
portalproduct/brik-client-portalportal product theme

Three rules govern every ID:

  • Lowercase kebab-case.
  • Never equals a reserved word (see below).
  • Never prefixes another registered ID.

The Component tier's ID is always bds. Only BDS authors components, so only BDS authors Component tokens. A tenant declares only what its Brand Kit Library owns — colour ramps, brand Semantic roles, font families. Foundations scales (space, size, border-width, …) are bds only.

An agent may propose token values, never token names and never IDs. A new ID is a brik-bds pull request to the registry, reviewed by a human. Inventing a plausible-looking ID or name is the failure that rolled back portal #512 / #553.

The first body segment names the tier

Every token now starts with the same ID, so the segment after it is what tells the tiers apart:

  • color or a scale word → Primitive
  • a purpose or role word (text · surface · background · border · page · gap · …) → Semantic
  • anything else → a Component stem

So a component stem may not begin with a reserved word. The closed lists are reservedWords in the grammar file. Two live knobs break the rule and are renamed in #2670: --bds-text-area-min-width reads as Semantic text, and --bds-page-padding-inline reads as Semantic page.

Tier

Every token sits at exactly one Tier. Higher tiers reference lower tiers via var() — a Semantic resolves to a Primitive, a Component --bds-* knob to a Semantic. A Semantic aliasing another Semantic is off-model with one exception: a color role-alias, where the target resolves to a --color-* Primitive (e.g. --border-focus: var(--border-brand-primary)). Color is the theme-varying axis, so the alias theme-tracks for free; a non-color Semantic alias (spacing/size/type) instead parasitizes a scale ladder and is banned. scripts/lint-token-tiers.mjs enforces this (ADR-035, broadening the ADR-025 narrow gate). Components only consume the upper tiers.

┌─────────────────────────────────────────────────────────┐
│  Tier 4 — Component    (component-scoped knobs)         │
│  --bds-{component}-{property}: var(<Semantic>)          │
│  Example: --bds-toast-shadow: var(--shadow-md)          │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │ var()
┌─────────────────────────────────────────────────────────┐
│  Tier 3 — Semantic     (what components consume)        │
│  body: {purpose}-{role}[-{state}]                       │
│  Example: --background-brand-primary:                   │
│             var(--color-poppy-500)                      │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │ var()
┌─────────────────────────────────────────────────────────┐
│  Tier 2 — Primitive    (named raw values)               │
│  body: color-{family}-{step} · {scale}-{step}           │
│  Example: --color-poppy-500: #e35335                    │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │
┌─────────────────────────────────────────────────────────┐
│  Tier 1 — Raw          (literal hex / numeric value)    │
│  Not a CSS variable — the literal value before naming.  │
│  Example: #e35335                                       │
└─────────────────────────────────────────────────────────┘

Tiers 2 and 3 show their body; the target name is --{id}-{body}. The examples are names in use today (see ID and the token shape).

What lives at each Tier

TierBody (after the --{id}-)SourceComponents reference it?
RawLiteral #e35335 — no name, no IDFigma swatch / hex chosen by designNo — never in component CSS
Primitivecolor-{family}-{step} (color), {scale}-{step} (font-size-{step}, space-{step}, …)Auto-generated from Figma Variables → Style DictionaryNo — never in component CSS (use Semantic)
Semantic{purpose}-{role}[-{state}] — Color foundations vocabularyAuto-generated; a tenant declares its own values under its own ID and binds the BDS name to them (below)Yes — this is the contract
Component{component}-{property}, ID always bdsAuthored in component / blueprint CSS; runtime values set by the component's JS/TSXYes — but only as a knob: it must resolve to a Semantic token (see below)

Components consume Semantic tokens, never Primitives directly. A button's CSS references --background-brand-primary, not --color-poppy-500. Why: Semantic is the layer that varies per brand. Bypassing it locks the component to one brand.

How a tenant binds BDS names

After #2670, components read only --bds-* names. A tenant does not silently redefine a BDS name with its own value. It declares its values under its own registered ID, then binds each BDS Semantic name it changes, in @layer client-theme (ADR-043 § 6):

@layer client-theme {
  .theme-vale-partners {
    --vale-partners-color-{family}-500: /* brand value */;
    --vale-partners-background-brand-primary: var(--vale-partners-color-{family}-500);
    --bds-background-brand-primary: var(--vale-partners-background-brand-primary);
  }
}

The cascade mechanism is unchanged: @layer client-theme still wins over @layer bds-tokens. What changes is that the override is a visible, greppable binding from one system's name to another's, not an anonymous re-declaration. This replaces the earlier "client themes override values with the same names" contract.

About Tier 4

The Component tier is --bds-{component}-{property} — a component- or blueprint-scoped custom property, where {component} is the component/blueprint identifier and {property} is the styled aspect (--bds-toast-shadow, --bds-slider-thumb-shadow, --bds-hero-img-card-bg). Its ID is always bds. This is the only sanctioned Component-tier namespace (ADR-014).

A Tier 4 token serves one of two roles, both under the same name:

  1. Override knob — a hook a component reads so a consumer can re-skin one instance without forking the CSS. It must resolve to a Semantic token, never a raw value:

    /* ✅ knob resolves to a Semantic token */
    box-shadow: var(--bds-toast-shadow, var(--shadow-md));
    
    /* ❌ raw literal in the fallback — reintroduces an off-token value
       that ships silently if the token fails to resolve, and lint can't see it */
    box-shadow: var(--bds-toast-shadow, 0 4px 12px rgba(0, 0, 0, 0.08));
  2. Runtime binding — a value the component's JS/TSX sets at render (e.g. --bds-slider-percent), read by the CSS to position or size an element.

Tier 4 never terminates at a raw value. A knob's fallback is a Semantic token; a runtime binding is computed from one. A literal inside a var() fallback on a tokenized property is Tier-1 leakage and is flagged by scripts/lint-tokens.js. The --bds- prefix is mandatory — --bp-* and bare --{component}-* are retired (see Drift patterns below).

--bds-* is for tokens BDS authors and reads — there is no consumer-authored Component namespace (ADR-034). A consumer that needs to restyle a BDS component sets a BDS-defined --bds-{component}-{property} hook (or uses a BDS variant) — it never invents its own token name. (For Primitive and Semantic tokens a tenant prefix is legitimate only as a registered ID followed by a grammar body — see The ID registry.) A consumer styling its own markup writes plain local CSS, which is outside the token system and needs no --bds-/--brik- prefix and no BDS blessing. The drift table's "migrate to --bds-*" remedy (below) is for BDS-authored knobs that shipped without the prefix — a consumer override migrates by moving into a BDS hook/variant, not by prefixing an invented name.

Anatomy

A token's name is --{id}-{body}. The formulas below are the body of each Tier. A body is read left to right, but its first slot answers a different question at each Tier — cross-applying one tier's formula to another is the single most common naming mistake. (Until #2670 ships, Primitive and Semantic names in dist/tokens.css are the body alone.)

Slot order by tier

TierFormulaFirst slot namesExample
Primitive (color)--color-{family}-{step}the reserved word color, then a family--color-poppy-500
Primitive (scale)--{scale}-{step}a scale — named for the CSS property it feeds--font-size-100, --space-400
Semantic (color)--{purpose}-{role}[-{state}]a purpose (page / surface / background / text / border)--background-brand-primary
Semantic (non-color)--{role}-{step}a semantic role--gap-md, --heading-xl
Component--bds-{component}-{property}the component--bds-toast-shadow

The first slot differs by tier — don't borrow one tier's order for another. At the Component tier the first slot is the component, not a purpose: --bds-footer-surface (component footer, property surface) is correct; --bds-surface-footer — "corrected" to a purpose-first order borrowed from the color Semantic form — is drift. The two Semantic formulas differ too: color leads with a purpose, non-color leads with a role.

The color Primitive row is closed on both slots, read from tokens/naming-grammar.json (tiers.primitive.color):

  • {family} is one of blue · grayscale · green · orange · pink · poppy · purple · tan · yellow.
  • {step} is one of 50 · 100 · 200 · 300 · 400 · 500 · 600 · 700 · 800 · 900 · 950 — numeric only (ADR-043 § 4). The word steps lightest · lighter · light · dark · darker · darkest · base retire from every tier and every system.

If the grammar file and this list disagree, the file wins.

Color anatomy

Color Semantic tokens follow the one Semantic formula, --{purpose}-{role}[-{state}]. Its {role} slot expands one of two ways — an intent form or a scoped-intent form:

Semantic:       --{purpose}-{role}[-{state}]

{role} slot, intent:         {role}[-{modifier}]
{role} slot, scoped intent:  {scope}-{scope-value}[-{modifier}]

Each segment is fixed vocabulary:

SegmentAllowed valuesExample
purposepage, surface, background, text, border--background-…
roleClosed list — see Color foundations vocabulary--background-brand-primary
scope / scope-valueA named context that carries its own hue — service-{line}, presence-{state}--background-service-marketing
modifier (optional)Context / tone / hierarchy — see Color modifiers--background-service-brand-on-light
state (optional)hover, pressed, disabled--background-brand-primary-hover

color is not a purpose. It is the reserved word that opens a color Primitive (--color-{family}-{step}), so a Semantic name never begins with it.

Status, service-line, and presence semantic tokens use the scoped-intent form — the scope (service-marketing, presence-online) already names the hue, so they never also take a -brand/-primary role segment. Everything else uses the plain intent form.

Tenant colour

A tenant's brand colour ramp is --{tenant-id}-color-{family}-{step}: a registered ID (the registry), then the same color Primitive body, with numeric steps. A tenant binds the BDS Semantic names it changes to its own ramp, in @layer client-theme (how a tenant binds). The --color-system-* names that ship today are not a tenant precedent — identity in a name is the ID's job now.

Token names without a purpose prefix are drift. Examples retired by brik-bds#712: --brand-primary (missing purpose; legitimate form is --{purpose}-brand-primary), --theme-brik-* (entire namespace violates the anatomy). If you see something shaped --brand-X or --theme-X-Y in old code, it's drift and should not be referenced.

When to add a hierarchy modifier

Never preemptively. A role starts as a single-level token set — one token, no hierarchy modifier (--text-service-marketing, which ships with no -primary / -secondary sibling). Add a -primary / -secondary pair only when the design carries a genuine second emphasis level within that role, distinct from the generic --text-secondary / --text-muted that already exist for de-emphasis.

/* ✅ single-level — one emphasis, no hierarchy modifier (real token) */
--text-service-marketing

/* ❌ preemptive — no --text-service-marketing-secondary exists to pair it,
   so the -primary suffix is just a longer name for the single-level token */
--text-service-marketing-primary

The pair earns its place only when a second emphasis tier actually ships alongside the first; until then, --text-secondary / --text-muted already cover generic de-emphasis. Scoped tokens never stack a hierarchy modifier under a context modifier either — the (text, surface) pairing carries emphasis implicitly, so --text-service-brand-on-dark is complete without a -primary.

The modifier slot's light/dark vocabulary (on-color, -on-light/-on-dark, -inverse, tone -light/-dark) is where four look-alike concepts diverge — documented in full at Color → Color modifiers.

Non-color anatomy

Half the registry is not a color. 325 of the 701 token names in dist/tokens.css sit outside the color purpose list above — --font-size-100, --gap-md, --ease-spring. They are a second legitimate formula, not drift. The distinction the color form draws between purpose and role does not apply, because there is no hue to place: a length, a duration, or a weight is fully described by the property it feeds and the step on that property's scale.

Primitive:  --{scale}-{step}         --font-size-100, --border-radius-400
Semantic:   --{role}-{step}          --gap-md, --heading-xl, --ease-spring

The first slot is a scale at the Primitive tier (named for the CSS property it feeds) and a semantic role at the Semantic tier. Both may be hyphenated (font-line-height, content-width) — the slot is the whole property or role name, not the first segment.

Primitive tier scales

SlotStep vocabularyFeeds
--font-size-*numeric (100 = 16px)font-size
--font-weight-*word (semibold)font-weight
--font-family-*role (body, heading)font-family
--font-line-height-*word (normal, snug)line-height
--font-casing-*word (uppercase)text-transform
--letter-spacing-*word (wide)letter-spacing
--space-*numeric (400 = 16px)any spacing property
--size-*numeric + pill / circlewidth / height
--border-radius-*numeric + t-shirtborder-radius
--border-width-*numeric + t-shirtborder-width
--shadow-blur-* · --shadow-offset-* · --shadow-spread-*numericthe parts of a box-shadow
--blur-radius-*t-shirtfilter: blur()
--duration-* · --delay-* · --iteration-*numerictransition / animation
--breakpoint-*word (desktop)media queries
--aspect-*ratio (16-9)aspect-ratio

Semantic tier roles

SlotStep vocabularyWhat it names
--gap-* · --padding-*t-shirtspacing by intent, not by pixel step
--heading-* · --display-* · --body-* · --label-* · --subtitle-*t-shirtthe type ramp per text role
--icon-*t-shirticon sizes, aligned to the type ramp
--shadow-* · --box-shadow-*t-shirtelevation
--ease-*word (spring)easing curves
--content-width-*t-shirtthe one horizontal-cap ladder — bands and text columns (ADR-041)
--measure-*t-shirtdeprecated — aliases to --content-width-text-*; drops next minor (ADR-041)
--state-*state (hover, disabled)interaction overlays and opacities
--section-*property (padding-block)the layout tier's device-fluid section band (ADR-042)

Two first segments carry both formulas. --border-* is 20 color tokens and 38 length tokens (--border-radius-*, --border-width-*); --shadow-* is elevation and the primitive parts (--shadow-blur-*, --shadow-offset-*, --shadow-spread-*). Match on the longest slot, never the first segment — --border-radius-100 is a length, not a color.

Named exceptions

Two kinds of thing live outside the tables above. Both are in the registry today; neither should be referenced in new work.

Renames owed — tracked on #1910, sequenced behind the naming ADR.

Shipping nameWhy it's driftUse instead
--tooltip-background, --tooltip-textComponent tier without the mandatory --bds- prefix — the retired --{component}-{prop} pattern.--bds-tooltip-*
--easing-ease-in, --easing-ease-out, --easing-ease-in-outStyle Dictionary's primitive export. A second name for one concept, at different curve values, and the easing-ease- stutter is the tell. Zero references in components/; --ease-* carries all 70.--ease-in / --ease-out / --ease-in-out

Slotless by exception — --web, --tablet, --mobile (tokens/figma-tokens.css:319-321) carry no purpose slot and are a deliberate carve-out, not a rename target. They are unitless leftovers in the primitives collection that no longer exist in Figma, and they are unconsumed — zero var() references anywhere in brik-bds, brik-client-portal, or brikdesigns (measured 2026-08-20). Deleting them rather than renaming them is tracked under #2595. Mirrored in EXCEPTIONS at scripts/__tests__/inspect-widget-tokens.test.mjs.

--breakpoint-* is a separate case and not slotless — breakpoint is its slot. It also records zero var() references, but that is by design, not dormancy: a custom property cannot appear inside an @media condition, so the CSS family carries the values for Figma and the docs while the breakpoints / mediaQueries TS exports carry them into conditions (ADR-025 §4). "Should BDS ship breakpoint tokens at all" was the open question here; #2591 answered yes and made all five rungs Figma-backed.

Color steps are settled; non-color steps are not. Color Primitive steps are numeric 50–950 (ADR-043 § 4), and the word steps retire in #2670. For the rest, step vocabularies are not settled here. Nine slots draw from more than one — --border-width-* carries numeric (100), t-shirt (md), and thin/standard/bold; --duration-* carries both 200 and fast. A second kind of split the census does not flag, because both halves are t-shirt: --icon-* spells the same step tiny in one place and 2xs in another. Which scale is canonical per family is #1910 axis 5, decided in the naming ADR. Run npm run lint-token-purpose-slots -- --census for the current measurement.

Colour families beyond the numeric ramps

lint-naming-canon rule 7 fails any --color-* in dist/tokens.css that does not parse as color-{family}-{step}, or match a family registered in tokens/naming-grammar.json. Beyond the nine numeric ramps, the grammar registers four named groups. Each exists for one purpose, stated in the grammar as $purposes. A colour that does not serve that purpose belongs in a different family, not in this one.

GroupNamesPurpose (one sentence)Rule
--color-system-*17: red, green, yellow, blue, purple, orange, neutral, their -light tints, and red/green/blue-surfaceMessaging. Error, success, warning, neutral and info colours for Alert, Badge and Toast, plus link blue and accent hues.A component never reads --bds-color-system-*; it reads the Semantic that aliases it (--bds-text-error). Enforced by lint-tokens rule 18 (#2689).
--color-social-*10: youtube, twitter, facebook, instagram, linkedin, yelp, google, apple, bing, tiktokThird-party brand marks. The platforms' own colours, used by SocialIcon.Not Brik colours; no brand kit rebinds them. Moved out of system in #2689.
--color-annotation-*6: pink, purple, blue, light-pink, light-purple, light-blueAnnotation tooling. Mark-up colours for design and doc annotation, never product UI.Registered as the annotation family (#2677).
--color-grayscale-black, --color-grayscale-white2Brand-guide-invariant constants. Foundation black and white that every brand guide carries.A brand kit never rebinds them. Registered as namedSteps on grayscale (#2677).
--color-system-transparent, --color-system-lightbox2Utility constants (#0000, a dark scrim) declared in tokens/gap-fills.css.Not messaging. Registered as namedExceptions in the grammar, names unchanged (#2689).

The retired word-step ramp rungs (--color-poppy-light, 54 names) live only in the generated bridge (#2670); the old --color-system-youtube spelling and the nine others resolve through tokens/compat/prefix-bridge.css.

Outside the colour grammar

lint-naming-canon rule 7 fails any --color-* in dist/tokens.css that does not parse as color-{family}-{step}. Of 190 distinct --color-* names (measured 2026-10-01), 99 parse; 91 do not. Each is baselined to the issue that removes or reshapes it, and none should be referenced in new work.

GroupCountShapeDisposition
Word-step ramp rungs54--color-{family}-{lightest|lighter|light|dark|darker|darkest} — 9 families × 6 wordsRetire. Numeric steps replace them (#2670)
--color-system-*29--color-system-red, --color-system-blue-surface, …Registered: system is a named family in tokens/naming-grammar.json (#2677)
--color-annotation-*6--color-annotation-pink, --color-annotation-light-blue, … (was annotation-annotation-*)Renamed and registered as the annotation family (#2677)
--color-grayscale-black, --color-grayscale-white2a numeric family with named endpointsRegistered as namedSteps on grayscale (#2677)

This section is gated: npm run lint-token-purpose-slots fails when a slot ships in dist/tokens.css that is neither named above nor dispositioned as drift. That is the only thing keeping the tables from going stale — a color-only formula describing half a system is exactly how #1910 started.

Role discipline

A token names a role, and two roles can resolve to the same value today without being the same role. --subtitle-md and --body-xs both alias --font-size-50; --subtitle-lg and --body-md both alias --font-size-100. That coincidence is a fact about this theme's current type ramp, not a licence to substitute one for the other.

Pick the token whose role matches the content, even when the values coincide. A subtitle uses --subtitle-*; reading copy uses --body-*. Values converge and diverge as the ramp is retuned — reference the role and you track every retune for free; reference the same-valued alias and you silently desync the moment those two steps split. Board.css and Table.css already hold this line ("Uses --subtitle-md for correct semantic sizing"), and it is why InteractiveListItem reaching for --body-xs on its subtitle slot is drift, not an equivalent (#2384).

A same-valued alias is invisible until the values split — nothing renders differently the day you pick the wrong role, so the miss ships clean and surfaces only on the next ramp change. The role name is the only thing that carries the intent forward; the pixel value does not.

How the seven concepts map to a real token

Take --background-brand-primary (the hot path):

ConceptAnswer
Anatomy--{id}-{body}, where the body is {purpose: background}-{role: brand-primary}
IDbds (the default Brik Brand Kit). The name carries no ID segment today; after #2670 it is --bds-background-brand-primary
TierSemantic
LibraryFoundations Library defines the bds name and its default value; each Brand Kit Library declares the tenant's own values under the tenant's ID
Layerbds-tokens carries the bds default; client-theme carries the tenant's binding of the bds name to its own (how a tenant binds)
ModeVaries by color mode (light / dark)
TenetFoundation (token canon) + Theming (tenant binding)

Every legitimate token answers all seven. If a token doesn't fit any one of them, it's drift.

Drift patterns to avoid

Drift patternWhy it's wrongRetired in
--theme-{name}-* (--theme-brik-green, --theme-blue-blue-light)Doesn't match Anatomy — no purpose prefix. Duplicate alias of a --color-{family}-{step} primitive.brik-bds#712 / #727
--brand-{tier} (--brand-primary, --brand-tertiary)Brand-tier names without purpose prefix. Legitimate composed form is --{purpose}-brand-{tier}.brik-bds#712 / #727
--surface-paper, --text-on-ink, --surface-soft, --surface-warmInvented modifiers or invented role values that aren't in the closed Color foundations vocabulary. Always parallel taxonomy.portal #512 / #553 (rolled back)
Component CSS reading --color-{family}-{step} directlyBypasses the Semantic tier. Locks the component to one brand.Caught by scripts/lint-tokens.js
--bp-{blueprint}-{slot}-{prop} (--bp-hero-img-card-bg)Parallel Component-tier namespace that shipped ahead of canon. Invisible to lint because it's an undefined reference, not a real token.#1043 / ADR-014 → migrate to --bds-*
--{component}-{prop} (--toast-shadow, --select-chevron-color)Component knob without the --bds- prefix. Collides with the global namespace and isn't lint-recognized.#1043 / ADR-014 → migrate to --bds-*
Raw literal inside a var() fallback on a tokenized property (var(--shadow-md, 0 4px 12px …))Reintroduces a Tier-1 value that ships silently if the token fails to resolve; lint sees only the canonical name and passes.Caught by scripts/lint-tokens.js (fallback-literal rule, #1043)

Inventing parallel taxonomy is the failure mode this vocabulary exists to prevent. Three rollbacks (portal #512, portal #553, brik-bds#712 cleanup) trace to agents inventing names that almost fit.

Verifying a token before referencing it

# Is this name in the canonical registry?
rg '\-\-{token-name}' node_modules/@brikdesigns/bds/dist/tokens.css

# Or run the consumer-side gate
./scripts/token-audit.sh --canonical-only

If the gate is missing or the name isn't found, stop. Don't ship a var() call against a name that doesn't exist — if it isn't in dist/tokens.css, it isn't real.

  • The cascade — Layer ordering, modes, color foundations vocabulary
  • Color — the full color registry and the color-modifier vocabulary (on-color, -on-light/-on-dark, -inverse, tone) the modifier slot draws from
  • Theming — how a client brand overrides these Semantic tokens per the four Theming Dimensions; the "How Content feeds Theming" section separates theming layers from content vocabularies
  • Composition Layers — the pedagogical Blueprint → Layout → Container → Block or Control → Component model that composes tokenized primitives
  • ADR-043 — System ID and the naming grammar — the ID, the shape, and the closed word lists this page copies
  • Figma Library architecture — Foundations Library vs Brand Kit Library
  • Color Pairings — the accessible foreground/background pairing system the on-color and -on-light/-on-dark modifiers feed into, with the contrast matrix and CI gate
  • Color primitive tiers — the closed tier vocabulary used by the portal theme generator
  • Slot Vocabulary — BEM slot vocabulary (different domain — component class names, not token names)
  • Style Dictionary build pipeline — how Figma → Library → Layer → CSS happens

On this page

💬