Primitive color palette
themes specs/themes/palette.kmd
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).
When this spec applies
Primary triggers
- Choose any color value in the Koder Stack
All triggers
- Pick a concrete color value for any Koder UI, icon or brand surface
- Decide a product's dominant hue or an icon gradient
- Define a tint/shade/state color (hover, pressed, disabled, subtle bg)
- Add or adjust a named hue or tonal step
Specification body
Spec — Primitive color palette
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 | Marketing/landing only (scope split per its §R1 — NOT merged here). Gains ramps the same way if/when needed. |
| Seed/map/alias 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 | Depth/platform; cloud/compute; 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 | error/danger (semantic); mail/urgent; 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)
- Ratify the 11-hue set + L-band targets (or tune).
- Confirm domain→hue map (R4) and product→hue map (R5).
- Whether
color-roles.kmdshould be migrated to reference primitive steps 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) |
References
meta/brand/koder-design/palette/primitives/primitives.jsonmeta/brand/koder-design/palette/primitives/generate_primitives.pyspecs/themes/color-roles.kmdspecs/themes/verge.kmdspecs/themes/editorial-palette.kmdspecs/icons/products.kmdrfcs/design-RFC-007-token-hierarchy-seed-map-alias.kmdrfcs/design-RFC-006-cross-language-token-distribution.kmd