Color — Advanced customization

How tenants + users customize the Koder color system beyond preset picking — per-role overrides, brand color injection, accessibility presets, custom error/warning hues. Material parity (`/styles/color/advanced/overview`). Read after `color-schemes.kmd` + `color-roles.kmd`; this spec covers the escape hatches.

Spec — Color: Advanced customization

Facet *isual*do Koder Design. Material parity: https://m3.material.io/styles/color/advanced/overview.

Levels of customization

Level Who Where stored Spec
*0* Preset pick from 12 canonical peruser / persurface color-schemes.kmd
*1* Dynamic from seed peruser / persurface color-dynamic.kmd
*2* Per-role override peruser / pertenant this spec
*3* Full custom scheme JSON per-tenant this spec
*4* Programmatic theme (SDK) per-app koder_kit API

Higher levels override lower. L2/L3 are the "advanced" escape hatches.

R1 — L2: Per-role override

Allows changing 1-2 roles while keeping the rest of the preset. Common cases:

  • Brand accent: override accent + accent-strong + accent-on
  • Brand error: override error to match brand-house color
  • Accessibility: bump focus to a higher-contrast variant

Override storage shape:

{
  "base_scheme": "default",
  "overrides": {
    "accent": "#E91E63",
    "accent-strong": "#AD1457",
    "accent-on": "#FFFFFF"
  }
}

When loading: start with the base scheme's tokens, then Object.assign the overrides. AAA contrast gates run on the final result — failures prompt the user to adjust.

R2 — L3: Full custom scheme

Tenant admin uploads a complete token bundle JSON (matches the shape of canonical preset entries from color-schemes.kmd Catalog):

{
  "name": "Acme Corp",
  "variants": {
    "light": {
      "bg": "#FFFFFF",
      "surface": "#F5F5F7",
      ...
    },
    "dark": {
      "bg": "#0B1220",
      ...
    }
  }
}

Validation pipeline:

  1. Schema check (all required tokens present)
  2. Hex format check (#RRGGBB)
  3. AAA contrast check on every required pair (per color-roles.kmd R4)
  4. Preview render at known canonical pages
  5. Owner approval gate (workspace admin signs off)

R3 — Brand color injection (tenant-scoped)

The most common L2 use case: a tenant wants their brand color threaded through every Koder app the tenant uses.

Mechanism:

  • Workspace admin sets brand color in Koder ID console
  • Koder ID propagates to all tenant-scoped apps via tenant_theme

    claim in the user's JWT

  • Apps merge tenant_theme onto the user's chosen preset at load

Precedence (lowest to highest):

  1. Surface default preset (per-device)
  2. Userchosen preset (peruser, synced)
  3. User dynamic seed (per-user)
  4. Tenant brand override (pertenant, applied via L2 on top of #13)
  5. Per-app explicit override (programmatic)

R4 — Color-blindness presets

Pre-built L2 override packs for common color vision differences:

Pack Override
Protanopia (red-blind) Shift error from red to magenta; success keeps
Deuteranopia (green-blind) Shift success from green to teal; error keeps
Tritanopia (blue-blind) Shift accent from blue to red-orange
Monochrome Map all hues to single accent; differentiate via tone

User selects in Settings § Accessibility. Stored per-user.

R5 — High-contrast preset

Special preset (high_contrast in canonical 12) that locks AAA contrast on all role pairs. Auto-activated when:

  • User toggles "High contrast" in Settings § Accessibility
  • OS reports prefers-contrast: more (Web, modern OS)
  • Forced colors active (Windows High Contrast mode)

Layered on top of any other L1-L3 customization (tightens, never loosens contrast).

R6 — Theme editor UI (advanced surface)

/playground/ on kds.koder.dev exposes a token paste editor today. Future "Theme Builder" (per #049.55) provides:

  • Seed color picker → live preview of all 18 roles
  • Per-role lock + override
  • Save as canonical-shaped JSON
  • Export buttons: CSS, SCSS, JSON, Dart, d.ts
  • AAA gate visual indicator per pair (red if fails)

Lives in koderwebkit's KoderThemeBuilder widget (planned).

R7 — Forbidden customization

Even at L3, the following are *ixed*to preserve product identity + baseline a11y:

  • Cannot disable AAA contrast gate (only high_contrast preset

    exists to TIGHTEN; never loosen)

  • Cannot set bg === text or any zero-contrast pair
  • Cannot remove a required role (only override its value)
  • Cannot change role semantics (e.g., can't make error mean

    "success" in your scheme — the role's meaning is fixed)

  • color-schemes.kmd — canonical 12 presets
  • color-roles.kmd — role taxonomy
  • color-dynamic.kmd — L1 seed-based generation
  • customization.kmd — overall customization axes
  • foundations/elements.kmd — which family uses which roles

Source: ../home/koder/dev/koder/meta/docs/stack/specs/themes/color-customization.kmd