Skip to content

Color — Advanced customization

themes specs/themes/color-customization.kmd

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.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Color: Advanced customization

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

Levels of customization

LevelWhoWhere storedSpec
L0Preset pick from 12 canonicalper-user / per-surfacecolor-schemes.kmd
L1Dynamic from seedper-user / per-surfacecolor-dynamic.kmd
L2Per-role overrideper-user / per-tenantthis spec
L3Full custom scheme JSONper-tenantthis spec
L4Programmatic theme (SDK)per-appkoder_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. User-chosen preset (per-user, synced)
  3. User dynamic seed (per-user)
  4. Tenant brand override (per-tenant, applied via L2 on top of #1-3)
  5. Per-app explicit override (programmatic)

R4 — Color-blindness presets

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

PackOverride
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
MonochromeMap 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 koder_web_kit's KoderThemeBuilder widget (planned).

R7 — Forbidden customization

Even at L3, the following are fixed 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

References