Overview
ANVIL uses two token layers: primitive (raw values) and semantic (usage meaning). Components must consume semantic tokens only.
Token Anatomy
How to read a semantic token
--c-bg-surface-muted |
Full token example |
|---|---|
-- |
CSS custom property prefix |
c |
Category: color |
bg |
Group: background role |
surface |
Context: component surface |
muted |
State/intensity modifier |
1. Primitive Tokens
Raw values with no intent. Naming: --p-{category}-{value}. Never used directly in components.
Categories
| Color | --p-red-50 … --p-red-900, --p-neutral-50 … --p-neutral-900, --p-gold-50 … --p-gold-900 |
|---|---|
| Spacing | --p-space-1 (4px) … --p-space-16 (64px), --p-space-11 (44px touch) |
| Radius | --p-radius-sm, --p-radius-md, --p-radius-lg, --p-radius-full |
| Typography | --p-ff-condensed, --p-ff-sans, --p-ff-mono; --p-fs-12 … --p-fs-40 |
| Border | --p-border-thin, --p-border-medium, --p-border-focus |
2. Semantic Tokens
Meaning-driven aliases by category prefix.
Categories and meaning
--c-* |
Color roles: --c-brand-primary, --c-text-base, --c-bg-surface, --c-border-subtle, --c-state-focus |
|---|---|
--sp-* |
Spacing scale and interaction size: --sp-1 … --sp-16, --sp-touch |
--r-* |
Radii: --r-none, --r-sm, --r-md, --r-lg, --r-full |
--ff-* |
Typography families: --ff-display, --ff-body, --ff-mono |
--fs-* |
Typography sizes: --fs-caption, --fs-small, --fs-body, --fs-lead, --fs-h4 … --fs-h1 |
Primitive → Semantic Mapping
Core examples
| --p-red-500 | → --c-brand-primary / --c-state-error |
|---|---|
| --p-neutral-900 | → --c-text-base / --c-brand-secondary / --c-state-focus |
| --p-space-11 | → --sp-touch |
| --p-ff-condensed | → --ff-display |
| --p-fs-16 | → --fs-body |
Rules
- Use semantic tokens only in components; primitives remain foundation-only.
- Do not use only color tokens. Spacing, radius, and typography must use
--sp-*,--r-*,--ff-*,--fs-*. - No semantic font size below 12px (0.75rem); body text minimum 1rem.
- When theming or high-contrast mode changes, update semantic mapping, not component CSS.
Naming Decision
We keep the --p-* prefix for now. Renaming primitives to plain --* would increase ambiguity and
create a breaking change across documentation, future exports, and tooling integrations.
If needed, we can introduce short aliases later with a deprecation phase.