Malevich

AI-Things / Skills / Skill/

Malevich foundations

Work with the three token tiers — how design decisions are encoded, which tier a component may consume, and how to pick the right token by role rather than by value.

foundations · basic · updated 2026-05-21

The one rule that matters most

A value in Malevich is never a literal. It is a token, referenced as var(--token-name). The only place raw values live is the foundations source. Everywhere else — components, applications, modifiers — consumes tokens. The malevich/no-raw-values lint rule enforces this.

If you're about to type a hex code, a pixel value, or a named color into a component or screen, stop and find the token instead.

The three tiers

Tokens form three layers, each referencing only the layer beneath it (T3 → T2 → T1). Strict separation is non-negotiable under v1.0 (ADR-0008). Read llm-wiki/architecture.md for the full model.

Tier 1 — foundations

Raw, unopinionated primitives: --palette-*, --scale-radius-*, --scale-spacing-*, --font-size-*, --font-weight-*, --motion-duration-*. They are the alphabet, devoid of meaning, and they live in packages/core/tokens/foundations.json.

You never consume foundations directly. Writing background: var(--palette-accent) in a component is an architecture violation, caught by malevich/no-foundation-direct-reference. Foundations exist only to be referenced by semantic tokens.

Tier 2 — semantic

Role-based tokens that carry meaning. This is the public vocabulary — the language you work in 95% of the time. Defined in packages/core/tokens/semantic.json. A semantic token's name describes what it is for, never what it is: --color-accent is right, --color-red is wrong.

Tier 3 — component-specific

Per-component derived tokens (--tab-background-active, --tab-text-active), in packages/core/tokens/component-specific.json. A refinement layer, not a replacement — most components need few or none. Components consume tier-3 where it exists, otherwise tier-2. Tier-3 references tier-2; never tier-1.

tier 3 (component-specific) → consumed by components
   ↓ references
tier 2 (semantic)           → consumed by components, apps, modifiers
   ↓ references
tier 1 (foundations)        → referenced by tier 2 only
   ↓ generated from
canvas.md                   → the project's taste, in markdown

The semantic vocabulary

These are the tier-2 families you'll reach for. (CSS custom property names are the dotted token paths with dashes — color.ink-strong becomes --color-ink-strong.)

color — role, never appearance:

font — semantic typography roles (ADR-0009), grouped by purpose. Each role names its purpose, not its size:

Each font token is a composite of five properties and expands into five CSS custom properties. There is no single font: shorthand var. Set them individually:

.card__title {
  font-family: var(--font-heading-title-family);
  font-size: var(--font-heading-title-size);
  font-weight: var(--font-heading-title-weight);
  line-height: var(--font-heading-title-line-height);
  letter-spacing: var(--font-heading-title-letter-spacing);
}

radius--radius-section, --radius-block, --radius-card, --radius-button, --radius-pill, --radius-field, --radius-tag, --radius-avatar, --radius-tooltip, --radius-dialog.

space — inset (padding) and gap families, each -s / -m / -l, keyed by layer: --space-inset-element-* (inside atomic elements), --space-inset-block-* (inside blocks), --space-inset-section-* (inside sections), and --space-gap-elements-* / --space-gap-blocks-* / --space-gap-sections-* for the gaps between them.

shadow--shadow-flat, --shadow-raised, --shadow-float, --shadow-overlay.

motion — durations --motion-fast / --motion-base / --motion-slow and easings --motion-easing-default / --motion-easing-in / --motion-easing-out.

Plus --size-control-{s,m,l,xl} for control heights and --border-width-{hairline,focus,emphasis}.

How to pick a token

Choose by role and intent, not by how it looks:

If no semantic token fits cleanly, that's a signal — either the design intent is unclear, or the system is genuinely missing a token. Do not invent a token mid-task. Use the closest existing one and flag the gap for a human to decide. New semantic tokens are a deliberate change to semantic.json, generated from canvas.md.

Theming, light, and dark

A theme is a set of foundation values satisfying the semantic contract. Light and dark are not separate themes — they are variants of every theme, selected by the data-color-scheme attribute on :root. Because components consume semantic tokens (which resolve differently per scheme), the same component CSS renders correctly in both modes with no component changes. Foundations and semantic tokens are generated from a project's canvas.md by @malevich/canvas; changing the canvas re-flows every visual surface without touching component code. To shape that taste from a reference, see the malevich-canvas-import skill.

Output checklist