Skip to content

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

All triggers

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

LegacyNew
/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

FlowInputOutput
Seed → paletteOne seed color18-role palette (light + dark) per color-dynamic.kmd
Manual overridePer-role color pickerTweaked palette + visible "out of WCAG" warnings
ImportPaste JSON, upload SVG / imageDetects 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
PaneWidth (Expanded ≥ 840 dp)Behavior on Compact
Inputs320 dp leftBottom sheet (modal)
PreviewflexFull width below inputs
Export320 dp rightBottom sheet (modal)

Mobile (Compact): preview is the main view; inputs and export reach via bottom sheets.

R2 — Inputs pane

Controls (top to bottom):

  1. Seed color picker (HCT-aware; shows hue / chroma / tone sliders + hex input)
  2. Theme mode segmented button: Light / Dark / System (default System — preview switches per OS)
  3. Preset dropdown (material3, ios_cupertino, gnome, etc. — matches customization.kmd L4 styling)
  4. Density segmented button: Compact / Default / Comfortable
  5. Shape scale preview row (modifying global radius scale)
  6. Brand override color picker (optional — overrides primary while keeping other roles algorithmic)
  7. 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:

SectionComponents
SurfacesContainer, Card (3 variants), Elevation levels
ControlsAll 7 button variants, Switch, Checkbox, Radio, Slider
InputText fields (Filled + Outlined), Chips, Date picker preview
FeedbackSnackbar, Banner, Progress indicator, Badge on icon
ContainersTop app bar, Bottom nav bar, Dialog mock
TypographyDisplay / 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":

FormatFile extensionContents
CSS vars.css:root { --kds-color-primary: #xxx; … } per role
SCSS map.scss$kds-colors: (primary: #xxx, …);
JSON tokens.jsonStyle Dictionary-compatible JSON
Dart const.dartconst kdsPrimary = Color(0xFFxxxxxx);
TS types.d.tsTyped token constants
Figma JSON.jsonFigma Variables import format
All formats (zip).zipBundle 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.kmd schema
  • ❌ Building on a 3rd-party color picker that isn't HCT-aware (we need HCT for palette extension)
  • themes/color-dynamic.kmd — HCT seed → palette algorithm
  • themes/color-roles.kmd — 18-role taxonomy
  • themes/color-customization.kmd — L0-L4 customization levels
  • tools/design-kit-export.kmd — Figma JSON export shared format
  • tools/contrast-checker.kmd — inline WCAG validator used here
  • foundations/customization.kmd — Settings tile contract

Implementation tracking

Tooling implementation is multi-day. Tracked separately:

  • projects/koder-stack#XXXtools/design-gen/internal/themer/ Go+WASM module
  • This spec is the contract; impl conforms to it.

References