Dynamic color
themes specs/themes/color-dynamic.kmd
Generate a complete Koder color scheme from a single seed color — user-supplied (brand) or extracted from a wallpaper / hero image. Material parity (`/styles/color/dynamic/user-generated-source`, aka "Material You"). Output: 12 canonical color-schemes presets' worth of tokens derived algorithmically from one input.
Quando esta spec se aplica
Triggers primários
- Generate a color scheme from a seed
Todos os triggers
- User supplies a brand color and wants the full palette generated
- Per-tenant theming from upload (wallpaper-derived theme)
- Implement a 'Pick your color' setting in app
Corpo da especificação
Spec — Dynamic color
Facet Visual do Koder Design. Material parity: https://m3.material.io/styles/color/dynamic/user-generated-source.
Marked
mandatory: false— dynamic color is an OPTIONAL surface feature; apps may ship with only the canonical presets and expose no seed-color picker.
What it does
Input: 1 seed color (hex, e.g. #3B5BFD).
Output: a full token bundle compatible with color-roles.kmd —
bg, surface, surface-variant, text, text-muted, accent,
accent-strong, accent-on, error, error-bg, etc. — for both
light and dark variants.
The token bundle plugs into the same renderer as the 12 canonical
presets (color-schemes.kmd Catalog), so widgets see no difference
between a dynamic scheme and a canonical one.
R1 — Tonal palette generation
From the seed:
- Convert seed RGB → HCT (Hue/Chroma/Tone) color space
- Extract Hue from seed
- Generate 13 tones (0, 10, 20, …, 95, 99, 100) at consistent chroma per role
- Bind roles to tones per the role-tone map (see R2)
Algorithm: based on Material Color Utilities (@material/material-color-utilities)
or equivalent HCT library. Koder ships a Dart port in koder_kit
- JS port in
koder_web_kit(planned).
R2 — Role-to-tone bindings
| Role | Light tone | Dark tone |
|---|---|---|
bg | 99 | 10 |
surface | 95 | 20 |
surface-variant | 90 | 30 |
text | 10 | 90 |
text-muted | 30 | 70 |
text-subtle | 50 | 60 |
accent | 40 | 80 |
accent-strong | 30 | 90 |
accent-on | 99 | 20 |
accent-tint | accent @ 12% | accent @ 12% |
error | (fixed hue) | (fixed hue) |
success | (fixed hue) | (fixed hue) |
warning | (fixed hue) | (fixed hue) |
info | (fixed hue) | (fixed hue) |
border | 80 | 40 |
focus | accent + saturated | accent + saturated |
Status colors (error/success/warning/info) keep fixed hues regardless of seed — preserves semantic meaning across themes (red error reads as error even with green-seeded brand).
R3 — Extraction from image
When the seed is an image (wallpaper, hero):
- Resize to 128×128 to bound compute
- K-means quantize to 32 colors
- Score each color: chroma × population × saturation
- Pick top-scoring color as seed
- Run R1 algorithm with that seed
Honors prefers-reduced-motion (no extraction animation) and runs
off main thread (Worker on web; isolate in Flutter).
R4 — Quality gates
Dynamic color must pass the same AAA contrast gates as canonical
presets (color-schemes.kmd). If the seed produces a palette that
fails any required pair:
- Auto-adjust: shift the failing role's tone toward higher contrast
- Or: reject the seed and prompt the user with the offending failure
- Never silently produce inaccessible output
R5 — Persistence
Dynamic scheme persists per-user (synced via Koder ID) when the seed source is the user's deliberate choice. Per-surface (local) when the seed comes from device wallpaper (which itself is local).
Storage key: koder.color_scheme.dynamic = {seed: "#...", source: "manual"|"wallpaper", generated_at: ISO8601}. The generated tokens
are reproducible from seed alone, so we don't store the full token
bundle.
R6 — Switching between dynamic and canonical
The color scheme picker in Settings shows:
- 12 canonical presets (cards with name + preview swatch)
- "Custom" option that opens a seed-color picker
- "Match device wallpaper" toggle (when supported by the platform)
Switching is instant; the new tokens replace via CSS custom property update (no full reload).
R7 — Supported platforms
| Platform | Wallpaper extraction | Manual seed | Notes |
|---|---|---|---|
| Android 12+ | ✅ (Material You system API) | ✅ | Native dynamic color hooks |
| iOS | ❌ wallpaper (sandbox) | ✅ | Manual seed only |
| macOS | ⚠️ accent color from OS | ✅ | Limited |
| Linux desktop | ❌ (no canonical API) | ✅ | Manual seed only |
| Windows 11 | ⚠️ accent color from OS | ✅ | Limited |
| Web | ❌ wallpaper | ✅ | Manual seed only |
Cross-link
color-schemes.kmd— canonical presets dynamic schemes drop intocolor-roles.kmd— role taxonomy dynamic schemes must satisfycustomization.kmd— Settings binding location
Referências
specs/themes/color-schemes.kmdspecs/themes/color-roles.kmdspecs/foundations/customization.kmd