Malevich

Architecture decision record

ADR-0008 · Token tier separation

ADR 0008 — Token tier strict separation

Status

Accepted, 2026-05-18.

Source: v1.0 architectural quiz outcomes (docs-internal/architecture/v1-master-diff-merge.md §3).

Context

The v0.1.0 token system defined three tiers — Foundations, Semantic, Component-specific — and documented that foundations "should not" be consumed directly by components. The word "should" did the wrong work: two of the ten existing components reach into foundations for edge cases (a custom hover shade, a one-off radius). Each violation appears reasonable in isolation; collectively they erode the abstraction:

v0.1.0 had no automated enforcement. The next 40+ components in v1.0 will compound the issue if we ship them under the soft rule.

Decision

The three-tier system gains a strict separation rule:

  1. Foundations are private to the semantic tier. No component, application, modifier, theme override, or example may reference a foundation token directly.
  2. Semantic is the only public token surface. External consumers read semantic tokens.
  3. Component-specific tokens reference semantic, never foundations. They are a refinement layer between semantic and a component's CSS, never a shortcut to the palette.
  4. Tier references flow downward only. A tier may reference the tier immediately below; no skipping (component-specificfoundations direct reference is forbidden), no upward references.
component CSS  →  component-specific  →  semantic  →  foundations
                                                   ↑
                                          (only path that crosses
                                           this boundary)

Enforcement

Three mechanisms make the rule mechanical, not aspirational:

  1. Lint rule no-foundation-direct-reference in @malevich/lint scans CSS files for var(--{foundation-name}) references in any file outside packages/core/tokens/semantic.json. Violations are build-blocking.
  2. Lint rule tier-respect validates that component-specific tokens (--button-bg) resolve through semantic (--color-accent), not through foundations (--color-neutral-50). v0.2.0 ships this as the strict variant of an advisory rule that existed in v0.1.0.
  3. CSS variable naming pattern. Foundation custom properties live under --{tier1-category}-* (e.g. --color-neutral-50). Semantic custom properties use distinct role-based names (--color-surface-raised). The naming pattern alone makes violations visible in code review.

What to do when semantic is insufficient

A component author needs a value that semantic doesn't offer. The correct path:

  1. First: propose a new semantic token. If the need is general ("a 'success-subtle' background"), it belongs in semantic.
  2. Second: if the need is component-specific ("button hover state needs a different shade than card hover"), add a component-specific token that references semantic.
  3. Never: reach into foundations directly. The temptation is highest here ("just use --color-accent-hover"). The rule is firm regardless of how reasonable the local case feels.

If neither path produces an acceptable value, the gap is a semantic tier deficiency. File an issue against @malevich/core; ship the component blocked until semantic gains the token.

Modifiers and themes

Modifiers and theme overrides bind to semantic tokens only. Modifier-generated component-specific values may compose semantic references, but never foundations:

/* ✅ allowed — modifier overrides component-specific via semantic */
[data-surface="elevated"] {
  --card-background: var(--color-surface-elevated);
}

/* ❌ forbidden — modifier references foundations */
[data-surface="elevated"] {
  --card-background: var(--color-neutral-50);
}

Themes (packages/core/themes/<name>/) override foundations only. A theme is a foundations replacement; the semantic layer above is shared.

Existing v0.1.0 violations

Phase 2 audit identified handful of direct foundation references in v0.1.0 component CSS. v0.2.0 migration includes:

The migration codemod cannot automate this fix because choosing the correct semantic token requires design intent. It is hand-work.

Consequences

Positive:

Negative / tradeoffs:

Alternatives considered

References