design-RFC-007 — Token hierarchy (seed / map / alias)
Surveys whether Verge should evolve from its current FLAT token model (`bg`, `surface`, `text`, `accent`, `border`, `focus` declared directly per preset) to a 3-tier hierarchy modeled on Ant Design — **seed tokens** (small input set), **map tokens** (derived by algorithm: light / dark / compact), **alias tokens** (semantic names consumed by components). Owner sign-off required to ratify; this RFC LAYS OUT the three options and trade-offs without picking one.
designRFC007 — Token hierarchy
*tatus* v0.1.0 Draft (2026
0522). Owner sign-off required before ratification. *mplementation deferred until ratified.*
R1 — Problem
Verge v0 uses *lat tokens*— every preset (canonicalPresets in internal/kinds/verge.go) declares the same handful of names (bg, surface, text, accent, border, focus) directly. The 44 presets × 2 modes (light / dark) = ~88 independent token sets, each hand-curated.
Properties:
| Property | Status |
|---|---|
| Easy to read a single preset | ✅ |
| Easy to add a new preset | ✅ — copy/paste then tune |
| Algorithmic theming (one seed → full palette) | ❌ |
| Compact / spacious density swap | ❌ |
| Cross-preset systematic invariants | ❌ — manual discipline only |
| Drift detection between consumer products | ❌ |
The flat model *hipped Verge v0*intentionally — minimalism (v0 = Adwaita 1:1). The trade-off was that systematic theming, density modes, and consumerextension ergonomics were deferred. This RFC reopens that trade-off for Verge v1.
R2 — Prior art
Ant Design — explicit 3-tier
Seed: colorPrimary = #1677ff, borderRadius = 6, fontSize = 14
↓ (algorithm: defaultAlgorithm | darkAlgorithm | compactAlgorithm)
Map: colorBgContainer, colorBgElevated, colorBorderSecondary, ...
↓ (rename for semantics)
Alias: colorBgLayout, colorTextDescription, colorBgSpotlight, ...Consumers consume *lias tokens* Theming = swap a few seeds. Dark mode = swap the algorithm. Compact = swap the algorithm. 5,000+ alias tokens generated from ~15 seeds.
Material 3 — partial hierarchy
Source HCT hue → Tonal palette (13 tones) → Role tokens
(primary, on-primary, primary-container, on-primary-container, ...)Less explicit than Ant — Material bakes the algorithm into the spec. Theming uses *ource color*as the single seed; tonal palette is implicit.
Base Web / Polaris — flat
Base Web (~80 components) uses flat tokens like Verge. Polaris ships flat with tone:* semantic names. Neither offers algorithmic derivation; consumers extend via Provider props.
Adwaita (Verge v0 reference) — flat
GNOME Adwaita uses flat library colors (accent_bg_color, card_bg_color, etc.). No seed/algorithm layer. Verge v0 inherited this directly.
R3 — Options
Option A — Keep flat (status quo)
*echanics* 44 presets × 2 modes continue as the contract. Each preset declares all alias tokens directly. New presets remain "copy/paste + tune".
*ros*
- Zero migration cost.
- Each preset is *ully auditable*as a single block of values
(no derivation hides surprises).
- Designers and devs can read any preset and see the literal
rendered values.
- Aligns with Verge v0 = Adwaita 1:1 philosophy.
*ons*
- Cross
preset invariants enforced only by audit (`vergecontrast-audit`,koder-spec-audit themes) — no structural guarantee. - Compact / spacious density mode is a *eparate preset family*
doubling the set.
- A consumer wanting a single-color brand override (e.g. red accent
for an emergency-services product) must fork a full preset.
Option B — Adopt 3-tier (Ant model)
*echanics*
- *eed tokens*declared in a new
internal/kinds/verge_seeds.go:seedAccent,seedNeutral,seedDanger,seedSurface,seedRadius,seedFontSize,seedSpacingUnit. - *lgorithms*in
internal/tokens/algorithms.go:lightAlgorithm,darkAlgorithm,compactAlgorithm. Each takes seeds and emits a map of derived tokens (color-bg-container,color-text-secondary,border-radius-sm, …). - *lias tokens*are renamed map outputs with semantic names; this
is what components consume.
*ros*
- Compact / spacious modes are *rivial*— swap algorithm.
- Brand override = swap one seed, propagation is automatic.
- 5–10× compression of preset definitions (44 presets become
44 seed sets).
- Cross-preset invariants are *tructural* not policed by audit.
- Theme drift detection across consumer products becomes
"does seed
Xderive to aliasYconsistently?" — trivially testable.
*ons*
- Migration cost — every existing preset must be re-expressed as a
seed set, then the derivation re-validated against the current literal values.
- A token's literal value is no longer readable directly — must
trace seed → algorithm → map → alias. Higher cognitive load.
- The algorithm becomes part of the contract; changing it changes
every consumer.
- More moving parts in
tools/design-genbuild chain.
Option C — Hybrid (seed → alias, no algorithm)
*echanics*
- *eeds*decompose alias values into "seed-derived" relations
(e.g.
--kdr-surface = lighten($seed-neutral, 8%)) but *o algorithm layer*sits between them. - Each preset declares seeds; alias tokens are computed via a small
fixed set of SCSS
like helpers (alpha`).lighten,darken, `with - No compact / spacious algorithm — density still ships as separate
presets.
*ros*
- Compresses preset definitions (~3–4× less code than flat).
- Still readable — every alias has a one-step derivation.
- Cross-preset invariants are structural (any seed change propagates).
- Migration risk lower than Option B (algorithm layer absent).
*ons*
- No compact / spacious benefit — density still copy/paste.
- Helper functions become part of the contract.
- Hybrid sits between "fully readable flat" and "fully algorithmic"
without the strengths of either.
R4 — Decision criteria
| Criterion | A — flat | B — 3-tier | C — hybrid |
|---|---|---|---|
| Migration cost | none | high (88 presets × derivation tuning) | medium |
| Readability of a single preset | ✅ literal | ❌ trace 3 layers | ◑ one-step derive |
| Brand-override ergonomics | ❌ fork preset | ✅ swap seed | ✅ swap seed |
| Density mode (compact/spacious) | ❌ separate presets | ✅ algorithm swap | ❌ separate presets |
| Cross-preset invariants | audit-policed | structural | structural |
| Consumer-extension cost | high (fork) | low (override seed) | low (override seed) |
| Risk of breaking existing consumers | none | high (every alias may shift) | medium |
| Adwaita 1:1 alignment (v0 philosophy) | ✅ | ❌ | ◑ |
R5 — Recommendation (advisory — pending owner sign-off)
*efer the decision.*Verge v0 is *hipping*under flat tokens with 44 presets and the audit pipeline holding cross-preset invariants ("good enough"). Re-opening the model now risks consumer breakage during the homologation acceleration phase.
When the trigger materializes — *irst product that needs a custom brand-override beyond preset swap*OR *irst request for compact / spacious density mode*— re-evaluate. At that point Option B is the strong default; Option C is a fallback if migration risk is too high.
R6 — Migration path (when triggered)
Whichever option ratifies, the migration MUST:
- Ship the new model *longside*flat —
verge.gokeeps emittingthe flat alias map for one Verge minor version (backwards-compat).
- Generate a delta report — "preset
X: seed derivation divergesfrom current literal by ΔE = 1.4 in dark mode" — for designer review before flipping.
- Update
koder_kit/koder_web_kitcross-language tokendistribution (RFC-006) to emit the new seedmapalias triple.
- Open per-consumer tickets to migrate hardcoded alias references
to the new names (if any drift).
- Remove the flat shim one Verge minor after the new model ratifies.
R7 — Test contract (when triggered)
- *1* For every flat preset, the new model derives values within
ΔE ≤ 2.0 of the original (visual equivalence).
- *2* All cross
preset invariants previously auditpoliced(WCAG AA / AAA contrast ratios, focus-ring contrast, etc.) hold structurally — i.e. the algorithm cannot emit a non-compliant map.
- *3* Compact algorithm swap halves the spacing unit consistently
across every alias that references it.
- *4* Dark algorithm swap inverts neutral lightness while
preserving accent saturation.
R8 — Open questions
- If Option B ratifies: is
compactAlgorithmshipped together withlightAlgorithm/darkAlgorithm, or is it a Verge v1.1 follow-up? - If Option B ratifies: does
internal/tokens/algorithms.goship aspure Go (one binary, design-gen owns it) or as a published library (multi
language consumers run the algorithm clientside viakoder_kitSDK)? - If Option C ratifies: which helper set is closed (
lighten,darken,mix,with-alpha, …)? OKLCHbased or sRGBbased?
Sign-off
| Role | Owner |
|---|---|
| Author | @rpm (2026 |
| Ratification | pending_ |
| Implementation backlog | tracked in tools/design-gen/backlog/pending/093-rfc-token-hierarchy-seed-map-alias.kmd |