Primitive color palette

v1.0.0 Ratified

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 accent or success (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 #3584E4blue-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 refines icons/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.kmd should 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)