Color roles

mandatory

Semantic color role taxonomy — the mapping from concrete tokens (bg, surface, accent, error, etc.) to the UI elements that use each. Material parity (`/styles/color/roles`). Companion to `themes/color-schemes.kmd` (which defines the actual colors) and `foundations/elements.kmd` (which says which family uses which role).

Spec — Color roles

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

A *ole*is a semantic slot ("primary action accent", "page background"). A *oken*is the concrete value bound to that role in the current theme. Roles stay constant across themes; tokens vary.

R1 — The 18 canonical roles

Organized by the 5 element families from foundations/elements.kmd:

Surface roles

Role Token Use
bg --kdr-bg Page background
surface --kdr-surface Cards, sheets, raised containers
surface-variant --kdr-surface-2 Recessed/inset surfaces (search box, code block)
surface-inverse inverse-bg Snackbars, tooltips (dark on light pages, light on dark)

Content roles

Role Token Use
text --kdr-text Primary text on bg / surface
text-muted --kdr-text-muted Secondary text, captions, helper text
text-subtle --kdr-text-subtle Disabled text, placeholders
text-on-accent --kdr-accent-on Text on accent-filled surfaces

Control roles

Role Token Use
accent --kdr-accent Primary action button, active link
accent-strong --kdr-accent-strong Hover/pressed state of accent
accent-on --kdr-accent-on Foreground on accent (text/icon)
accent-tint accent at 12% opacity Selected row background, focus halo

Status roles

Role Token Use
error --kdr-error Error state border/text
error-bg --kdr-error-bg Error banner background (subtle tint)
warning --kdr-warning Warning state border/text
success --kdr-success Confirmation badge, valid input
info --kdr-info Informational banner

Decoration roles

Role Token Use
border --kdr-border Container borders, dividers
border-strong --kdr-border-strong Emphasis borders (focused container)
focus --kdr-focus Keyboard focus ring color
selection --kdr-selection Text selection bg / row selection bg

R2 — Role binding contract

Every koder_kit widget MUST bind colors via roles, not raw hex:

// ❌
Container(color: Color(0xFF3B5BFD)) // hardcoded

// ✅
Container(color: KoderTheme.of(context).accent)

The theme exposes a typed KoderColorRoles struct (planned) with one getter per role. Widget code never references tokens by string.

R3 — Inversion in dark mode

Roles flip meaning between light and dark themes via the token system, not via per-widget logic. Example:

Role Light theme token Dark theme token
bg #FFFFFF #0B1220
text #0B1220 #E7ECF5
accent #3B5BFD #7E97FF (lighter for dark bg legibility)

Widget code stays the same; the theme rebinds the tokens.

R4 — Contrast requirements

Per color-schemes.kmd § AAA contrast, role bindings must satisfy:

Pair Min ratio
text/bg, text/surface 7.0 (AAA Normal)
text-muted/bg 4.5 (AA)
accent-on/accent 4.5 (AA)
error/error-bg 4.5
focus/bg 3.0 (UI element contrast)

high_contrast preset tightens all to ≥ 7.0.

R5 — Forbidden bindings

  • ❌ Body text on accent background (use accent-on instead)
  • text-muted for primary action labels (use text or accent-on)
  • ❌ Body text in error color (red text outside error context is alarming)
  • ❌ Custom hex on user-facing surface without going through a role

R6 — When to add a new role

Adding a role is an architectural decision (impacts every theme). Threshold:

  • Needed by ≥ 2 widgets
  • Not expressible via existing role + opacity modifier
  • Has a stable semantic meaning across themes

Process: propose via PR with the binding added to all 12 color scheme presets simultaneously (per color-schemes.kmd Catalog).

  • color-schemes.kmd — concrete tokens per preset
  • light-dark.kmd — light/dark switching behavior
  • foundations/elements.kmd — which family uses which roles
  • interaction/states.kmd — overlay/state colors derive from roles

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