Primitive color palette
The PRIMITIVE color tier of the Koder Design System: named hues, each with an OKLCH tonal ramp (50–900). It is the single machine-readable pool of color steps that semantic roles (`color-roles.kmd`), product icons (`icons/products.kmd`) and — if RFC-007 ratifies — the seed layer all draw FROM. Replaces ad-hoc per-asset hexes with references like `blue-600`. Does NOT presuppose the seed/map/alias algorithm (RFC-007 is draft/deferred); it only supplies the primitives. Source of truth: `meta/brand/koder-design/palette/primitives/primitives.json` (DTCG).
Tier: primitive (the "wheel"). This is the layer below semantic roles. A role like
accentorsuccess(color-roles.kmd) and a product icon's dominant color (icons/products.kmd) MUST resolve to a named step here (e.g.blue-600), never a free hex. This kills the "which blue / how deep" guesswork by making every color a reference.
Relationship to the existing color specs (no duplication)
| Layer | Owner spec | What it holds |
|---|---|---|
| Primitive (this spec) | palette.kmd + primitives.json |
Named hues × tonal ramps 50–900. The raw pool. |
| Semantic (map) | color-roles.kmd |
text / accent / error / success / warning / info … — each role POINTS to a primitive step. Unchanged; this spec only gives it concrete steps to point at. |
| UI palette (Verge) | verge.kmd |
In-product surfaces. Verge accent #3584E4 ≈ blue-500. |
| Editorial palette | editorial-palette.kmd |
Marketinglanding only (scope split per its §R1 — NOT merged here). Gains ramps the same way ifwhen needed. |
| Seedmapalias algorithm | rfcs/design-RFC-007 (DRAFT, deferred) |
IF ratified, its seed tokens draw from this primitive pool. This spec does not adopt the algorithm — it is orthogonal and useful regardless. |
R1 — Method (OKLCH, gamut-clipped)
Ramps are generated in OKLCH (Björn Ottosson) for perceptual uniformity — equal numeric steps look equally far apart, fixing the uneven-brightness jumps of naïve HSL. Each named hue is anchored by a 600-ish hex; the generator fixes a perceptual-lightness target L per step and scales chroma along a peak-in-the-middle curve, then clips chroma into the sRGB gamut where a step would otherwise be unrepresentable. Resolved sRGB hex is persisted (with the source oklch in $extensions) so non-CSS targets (RFC-006: 7 languages) get hex directly. Generator: palette/primitives/generate_primitives.py.
Per-step perceptual lightness L (OKLCH, 0–1):
| Step | 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 |
|---|---|---|---|---|---|---|---|---|---|---|
L |
.97 | .94 | .88 | .81 | .72 | .64 | .56 | .47 | .38 | .28 |
R2 — Named hues (the wheel)
11 hues: one neutral (slate) + a 10-hue chromatic wheel covering every product/domain need. Full step values + WCAG contrast live in primitives.json; this table is the symbolism/derivation index.
| Hue | Anchor (~600) | Derivation | Symbolism / use |
|---|---|---|---|
slate |
#5b6675 |
neutral cool gray | Surfaces, text, borders, dividers — the structural backbone. |
blue |
#3584E4 |
verge.accent (Adwaita) | Brand/UI primary; products domain; info. The default action color. |
indigo |
#4f46e5 |
verge.blue → indigo | Depthplatform; cloudcompute; deep-blue sibling. |
violet |
#7c3aed |
editorial.violet/plum | AI / data / creative; services domain; flagship signature option. |
magenta |
#db2777 |
editorial.magenta | Playful / navigation / discovery accents. |
red |
#dc2626 |
verge.red | errordanger (semantic); mailurgent; high-attention. |
orange |
#ea580c |
verge.orange | Warm action / craft; coral/mail gradient partner. |
amber |
#f59e0b |
verge.yellow → amber | warning (semantic); time/calendar; meta domain warmth. |
green |
#16a34a |
verge.green | success (semantic); hub/store; growth / go. |
teal |
#0d9488 |
verge.green → teal | engines domain; maps/location; fresh / raw power. |
cyan |
#0891b2 |
verge.blue → cyan | infra domain; operation / observability; cool signal. |
R3 — Usage matrix (which step goes where)
A band discipline that holds across all hues (because OKLCH L is constant per step). Light theme shown; dark theme mirrors (swap the band, keep the hue).
| Step band | Role | Examples |
|---|---|---|
50–100 |
Subtle fills / tints | Banner backgrounds, hovered-row tint, selected-tint, *-bg role variants. |
200–300 |
Hairlines / disabled | Borders, dividers, disabled controls, skeletons. |
400–500 |
Secondary / hover | Secondary accents, hover states, gradient light end, illustration mid-tones. |
600 |
Primary on light | accent, primary buttons, links, dominant icon fill. Floor: AA (≥4.5) on white. |
700–800 |
Pressed / deep | Pressed/active states, gradient deep end, emphasis on tinted bg. |
900 |
Ink / text | Body text on light, max-emphasis headings, deepest gradient stop. |
Contrast gate (hard): any text/icon step on its background MUST meet WCAG AA (4.5 normal / 3.0 large). primitives.json carries contrast_on_white / contrast_on_black per step; kicon validate and koder-spec-audit themes enforce. Roles in color-roles.kmd keep their existing contrast targets — this spec just makes the chosen step explicit.
R4 — Domain → signature hue (RFC-003 L1)
Each L1 domain gets a signature hue for domain-level identity (domain landings, console chrome, docs section accents). This is the systematic anchor; it does NOT force every product in the domain to that hue (see R5).
| Domain | Hue | Rationale |
|---|---|---|
products/ |
blue |
User-facing apps; the Verge brand-action color. |
services/ |
violet |
API / AI / data-narrative. |
engines/ |
teal |
Embedding / raw power. |
infra/ |
cyan |
Operation / observability. |
meta/ |
amber |
Human reading: docs, brand, sites — warmth. |
R5 — Product → hue alias (icon dominant color)
Product launcher icons need mutual distinctiveness on a home screen, so each product picks its own wheel hue (it does not collapse to its domain hue). What makes them read as one family — the #1 gap in #038 — is not a shared hue but a shared band rule:
Icon family rule. Dominant fill =
<hue>-600. Icon gradient runs<hue>-700(deep, bottom) →<hue>-500(mid) →<hue>-400(light, top). White symbol in negative space. All hues sit in the same OKLCH lightness band, so seven different hues still look like siblings. This supersedes the ad-hoc hexes of the 2026-06 proposal and refinesicons/products.kmd §1("cor derivada da paleta Koder") from prose to a concrete reference.
| Product | Hue | Dominant | Gradient (deep → light) |
|---|---|---|---|
| Koder Drive | blue |
blue-600 |
blue-700 → blue-500 → blue-400 |
| Koder Hub | green |
green-600 |
green-700 → green-500 → green-400 |
| Koder Mail | red |
red-600 |
red-700 → red-500 → orange-400 |
| Koder Cloud | indigo |
indigo-600 |
indigo-700 → indigo-500 → violet-400 |
| Koder Map | teal |
teal-600 |
teal-700 → teal-500 → green-400 |
| Koder Cal | amber |
amber-600 |
amber-700 → amber-500 → amber-400 |
| Koder Kruze | violet |
violet-600 |
violet-700 → violet-500 → magenta-400 |
The single-hue-per-product rule (
products.kmd §1) stays; the optional analogous-neighbor light end (e.g. Mail →orange-400) is the only sanctioned two-tone, and stays within one wheel-neighbor — not Google's full polychromy. New products: pick a free wheel hue; record it here.
R6 — Machine-readable source of truth
palette/primitives/primitives.json (DTCG, koder.* extensions) is authoritative; palette.kmd is the human mirror. Regenerate with generate_primitives.py (pure-Python OKLCH, no deps). Distribution to the 7 token languages rides RFC-006; Figma sync rides RFC-005. Never hand-edit the JSON hexes — change the anchors/curve and regenerate.
Open decisions (owner sign-off → ratify)
- [x] Ratify the 11-hue set + L-band targets (or tune).
- [x] Confirm domain→hue map (R4) and product→hue map (R5).
- [x] Whether
color-roles.kmdshould be migrated to reference primitivesteps now, or in a follow-up (this spec is non-breaking until then).
Sign-off
| Item | State |
|---|---|
| Method (OKLCH + gamut clip) | drafted 2026-06-03 |
| Primitive ramps generated | ✅ primitives.json (11×10) |
| Owner ratification | ✅ 2026-06-03 (owner) |