Theme Builder
tools specs/tools/theme-builder.kmd
Interactive in-browser tool at `kds.koder.dev/themer/` that turns a seed color into a full Material-3-style palette and exports tokens in every supported format. Replaces the legacy paste-JSON playground. Material parity (`/theme-builder`).
When this spec applies
Primary triggers
- Implement the Theme Builder
All triggers
- Let users tune a Koder Design theme without coding
- Replace the paste-JSON playground with a guided flow
- Export tokens directly from the live preview
Specification body
Spec — Theme Builder
Facet Tool of Koder Design. Material parity: https://m3.material.io/theme-builder.
URL: https://kds.koder.dev/{locale}/themer/ (canonical;
backwards-compatible alias /playground/ 301 → /themer/).
What it replaces
| Legacy | New |
|---|---|
/playground/ paste-JSON token editor | /themer/ guided seed-to-tokens flow |
Legacy playground stays reachable for power users who want raw JSON edit — but the Themer is the default entry point.
Three flows on one page
| Flow | Input | Output |
|---|---|---|
| Seed → palette | One seed color | 18-role palette (light + dark) per color-dynamic.kmd |
| Manual override | Per-role color picker | Tweaked palette + visible "out of WCAG" warnings |
| Import | Paste JSON, upload SVG / image | Detects color, runs Seed → palette |
User can move freely between flows; state is shared.
R1 — Layout (three panes)
┌──────────────┬────────────────────────────┬──────────────────┐
│ Inputs │ Live preview │ Export │
│ │ │ │
│ seed: ● │ ┌─ Buttons ─────────────┐ │ Format dropdown │
│ brand: ● │ │ Filled / Tonal / Out │ │ ├ CSS vars │
│ density: ● │ └───────────────────────┘ │ ├ JSON tokens │
│ │ │ ├ SCSS map │
│ Surface │ ┌─ Cards ──────────────┐ │ ├ Dart const │
│ scheme: ● │ │ Sample card content │ │ ├ TS .d.ts │
│ │ └───────────────────────┘ │ └ Figma JSON │
│ Preset: │ │ │
│ material3 ▾ │ ┌─ Form ───────────────┐ │ [Download .zip] │
│ │ │ Field [Submit] │ │ [Copy URL] │
└──────────────┴────────────────────────────┴──────────────────┘
320 dp flex-grow 320 dp
| Pane | Width (Expanded ≥ 840 dp) | Behavior on Compact |
|---|---|---|
| Inputs | 320 dp left | Bottom sheet (modal) |
| Preview | flex | Full width below inputs |
| Export | 320 dp right | Bottom sheet (modal) |
Mobile (Compact): preview is the main view; inputs and export reach via bottom sheets.
R2 — Inputs pane
Controls (top to bottom):
- Seed color picker (HCT-aware; shows hue / chroma / tone sliders + hex input)
- Theme mode segmented button: Light / Dark / System (default System — preview switches per OS)
- Preset dropdown (material3, ios_cupertino, gnome, etc. —
matches
customization.kmdL4 styling) - Density segmented button: Compact / Default / Comfortable
- Shape scale preview row (modifying global radius scale)
- Brand override color picker (optional — overrides primary while keeping other roles algorithmic)
- Advanced disclosure → per-role manual overrides
Each control change triggers preview recompute (debounced 100 ms).
R3 — Preview pane
Shows representative components rendered with the current tokens:
| Section | Components |
|---|---|
| Surfaces | Container, Card (3 variants), Elevation levels |
| Controls | All 7 button variants, Switch, Checkbox, Radio, Slider |
| Input | Text fields (Filled + Outlined), Chips, Date picker preview |
| Feedback | Snackbar, Banner, Progress indicator, Badge on icon |
| Containers | Top app bar, Bottom nav bar, Dialog mock |
| Typography | Display / Headline / Title / Body / Label scales |
Each component matches its specs/components/*.kmd anatomy exactly —
this preview IS canonical reference; specs reference back to it.
Mobile (Compact): horizontal accordion of sections; tap to expand one at a time.
R4 — Export pane
Single dropdown picks output format; click "Download" or "Copy":
| Format | File extension | Contents |
|---|---|---|
| CSS vars | .css | :root { --kds-color-primary: #xxx; … } per role |
| SCSS map | .scss | $kds-colors: (primary: #xxx, …); |
| JSON tokens | .json | Style Dictionary-compatible JSON |
| Dart const | .dart | const kdsPrimary = Color(0xFFxxxxxx); |
| TS types | .d.ts | Typed token constants |
| Figma JSON | .json | Figma Variables import format |
| All formats (zip) | .zip | Bundle of all above + README |
Also a Copy URL button: encodes current theme state in URL hash; sharing the URL replays the same theme in someone else's Themer.
R5 — Per-role override
In Advanced disclosure, expose each of the 18 roles
(color-roles.kmd § R1) as a color picker. Manual override
disconnects that role from the algorithmic palette; toggle
"Auto-derive" to reconnect.
When manual override fails WCAG contrast (contrast-checker.kmd),
show inline warning with the failing ratio + the role pair affected.
R6 — Persistence
Theme state persists in URL hash, NOT server-side:
- Hash format:
#seed=0xff5500&mode=dark&preset=material3&... - Pasting any Themer URL reproduces the theme
- No login required, no PII collected
LocalStorage caches the last theme so refresh restores it. Clear via "Reset to defaults" button.
R7 — Color-blindness simulation
Toggle in Inputs pane: simulate Protanopia / Deuteranopia / Tritanopia / Achromatopsia. Renders the preview through a CSS filter matrix.
When simulation is active, the contrast checker re-validates against the simulated palette and flags new failures.
R8 — Accessibility of the Themer itself
The tool itself follows ALL specs/components/*.kmd rules:
- Color pickers are accessible (HCT sliders are
role="slider") - Live preview region announces "Preview updated" politely on change
- Export buttons have full labels
- Keyboard: Tab cycles between Inputs / Preview / Export panes; Arrow keys within pane
R9 — Performance
- First paint < 1 s on 3G
- Re-render on seed change: < 100 ms (debounced)
- All palette math runs client-side (no server roundtrip)
- Color science (HCT, contrast, palette extension) lives in
tools/design-gen/internal/color/— single Go implementation compiled to WASM for the Themer
R10 — Out of scope (cleanly excluded)
- ❌ Per-component override (use per-role override + custom CSS in your app)
- ❌ Saving themes to a server account (use Copy URL)
- ❌ Theme marketplace / browse community themes
- ❌ AI-generated themes from a prompt (future RFC, not this spec)
R11 — Forbidden patterns (in implementations)
- ❌ Server-side rendering of palette math (defeats client-side privacy + offline)
- ❌ Re-flowing the entire preview on every keystroke (debounce required)
- ❌ Hiding the legacy
/playground/redirect (must 301 forever) - ❌ Exporting tokens that don't match
color-roles.kmdschema - ❌ Building on a 3rd-party color picker that isn't HCT-aware (we need HCT for palette extension)
Cross-link
themes/color-dynamic.kmd— HCT seed → palette algorithmthemes/color-roles.kmd— 18-role taxonomythemes/color-customization.kmd— L0-L4 customization levelstools/design-kit-export.kmd— Figma JSON export shared formattools/contrast-checker.kmd— inline WCAG validator used herefoundations/customization.kmd— Settings tile contract
Implementation tracking
Tooling implementation is multi-day. Tracked separately:
projects/koder-stack#XXX—tools/design-gen/internal/themer/Go+WASM module- This spec is the contract; impl conforms to it.
References
specs/themes/color-roles.kmdspecs/themes/color-dynamic.kmdspecs/themes/color-customization.kmdspecs/themes/typography.kmdspecs/themes/shape.kmdspecs/tools/design-kit-export.kmdspecs/tools/contrast-checker.kmd