Original Request
add a custom theme option to customize theme elements with a GUI
Summary
The dashboard's theme gallery offers eight curated themes, but their colors are hard-coded in CSS and cannot be modified. This issue adds a theme editor to the dashboard: edit the 17 colors of any theme's light and dark variants, create new custom themes, pick colors with a web color picker or hex input, and preview changes live. Changes default to browser-local storage; whether custom themes should instead live server-side (shared across browsers) is an open question to decide at assessment time.
Scope: dashboard only. The TUI theme system (terminal JSON themes) is out of scope.
Current Behavior
- The dashboard theme gallery (
packages/dashboard/src/client/components/theme-gallery.tsx) offers eight curated themes: entropist.ca (default), Dim, Solarized, Gruvbox, Caves of Qud, Van Gogh, Okabe-Ito, Paul Tol. Each theme is a family with a light and a dark variant.
- Each variant defines 17 named colors: background, text, border, muted, four status colors (running, attention, idle, error), and nine syntax colors (comment, keyword, string, number, function, type, variable, operator, punctuation), plus one font per theme.
- All palettes are hard-coded in
packages/dashboard/src/client/styles/themes.css (private --<theme>-l-* / --<theme>-d-* properties mapped onto the public tokens --bg, --text, --border, --muted, --status-*, --syntax-*, --mono-font).
- The chosen theme, color mode, and font persist in browser localStorage only (
dreb.dashboard.theme, dreb.dashboard.colorMode, dreb.dashboard.font) — no server or RPC involvement.
- There is no way to edit a theme's colors or add a new theme; no GUI for theme customization exists anywhere.
Proposed Behavior
- A theme editor UI in the dashboard, alongside the existing theme gallery.
- For any theme (curated or custom), edit all 17 palette colors of the light and dark variants using a web color picker and hex input.
- Create new custom themes: name the theme, start from a copy of an existing theme, then edit its colors.
- Live preview: while editing, the preview updates immediately; if the theme being edited is the active one, the page itself updates too.
- Saving: browser-local by default (localStorage, consistent with the current appearance persistence). Server-side storage (shared across browsers/machines) is an open design question — see Open Questions.
- Custom themes appear in the theme gallery alongside the curated themes and can be selected like any other theme.
Acceptance Criteria
- The dashboard's theme gallery includes a theme editor UI for customizing theme colors.
- For any theme (curated or custom), all 17 palette colors of the light and dark variants (background, text, border, muted, four status colors, nine syntax colors) can be edited via a web color picker or hex input. Invalid hex input is rejected with visible feedback (never silently written).
- The user can create a new custom theme by naming it and starting from a copy of an existing theme.
- Changing a color updates a live preview immediately, without saving.
- Edits and new themes persist across page reloads (in whichever storage location is chosen at assessment time).
- Custom themes appear in the theme gallery alongside the curated themes and can be selected.
Open Questions
- Storage location: browser-local (default working assumption — matches the current per-browser model, no server involvement) vs. server-side (custom themes shared across all browsers/machines). To be decided at assessment time; the Technical Notes outline the implications of each option.
Context
packages/dashboard/src/client/components/theme-gallery.tsx — current gallery UI (self-contained preview cards via data-theme scoping)
packages/dashboard/src/client/state/appearance.ts — theme catalog, signals, localStorage persistence, cross-tab sync
packages/dashboard/src/client/styles/themes.css — curated palettes (17 colors per variant, light/dark mapping rules)
packages/dashboard/src/client/styles/tokens.css — baseline tokens (default theme)
- Root
README.md, dashboard section — documents the current curated gallery
- The dashboard theme system is deliberately independent of the TUI theme system (terminal JSON themes with 51 color tokens — a separate system, out of scope here)
Technical Notes
- Palette structure: each theme block in
themes.css defines private --<theme>-l-* / --<theme>-d-* properties (17 each) and maps them onto the public tokens under [data-theme='x'] (light) and [data-theme='x'][data-color-mode='dark'] (dark). Edited values must flow through the same mapping so system/light/dark mode behavior is preserved.
- Browser-local persistence (default): follow the existing
state/appearance.ts pattern (guarded localStorage reads/writes, in-memory signal, cross-tab sync via storage events). Custom theme data (name + palettes) needs a new storage key plus a validation scheme.
- If server-side is chosen at assessment time: the dashboard server needs endpoints to list/get/save custom themes; the fixed theme-catalog allowlist (with contract tests asserting sync between
appearance.ts, the index.html bootstrap, and the themes.css scopes) must accommodate dynamic entries; also consider multi-user/multi-machine semantics and how curated themes stay editable-but-resettable.
- Live preview: the gallery's preview cards already scope
data-theme locally per card; an editor preview can reuse that mechanism with the edited palette applied inline.
- The WCAG AA contrast notes in
themes.css are authoring guidance; whether the editor enforces or merely reports contrast is a design decision.
- Consideration (not a requirement): a way to reset an edited curated theme back to its original colors.
Original Request
Summary
The dashboard's theme gallery offers eight curated themes, but their colors are hard-coded in CSS and cannot be modified. This issue adds a theme editor to the dashboard: edit the 17 colors of any theme's light and dark variants, create new custom themes, pick colors with a web color picker or hex input, and preview changes live. Changes default to browser-local storage; whether custom themes should instead live server-side (shared across browsers) is an open question to decide at assessment time.
Scope: dashboard only. The TUI theme system (terminal JSON themes) is out of scope.
Current Behavior
packages/dashboard/src/client/components/theme-gallery.tsx) offers eight curated themes: entropist.ca (default), Dim, Solarized, Gruvbox, Caves of Qud, Van Gogh, Okabe-Ito, Paul Tol. Each theme is a family with a light and a dark variant.packages/dashboard/src/client/styles/themes.css(private--<theme>-l-*/--<theme>-d-*properties mapped onto the public tokens--bg,--text,--border,--muted,--status-*,--syntax-*,--mono-font).dreb.dashboard.theme,dreb.dashboard.colorMode,dreb.dashboard.font) — no server or RPC involvement.Proposed Behavior
Acceptance Criteria
Open Questions
Context
packages/dashboard/src/client/components/theme-gallery.tsx— current gallery UI (self-contained preview cards viadata-themescoping)packages/dashboard/src/client/state/appearance.ts— theme catalog, signals, localStorage persistence, cross-tab syncpackages/dashboard/src/client/styles/themes.css— curated palettes (17 colors per variant, light/dark mapping rules)packages/dashboard/src/client/styles/tokens.css— baseline tokens (default theme)README.md, dashboard section — documents the current curated galleryTechnical Notes
themes.cssdefines private--<theme>-l-*/--<theme>-d-*properties (17 each) and maps them onto the public tokens under[data-theme='x'](light) and[data-theme='x'][data-color-mode='dark'](dark). Edited values must flow through the same mapping so system/light/dark mode behavior is preserved.state/appearance.tspattern (guarded localStorage reads/writes, in-memory signal, cross-tab sync viastorageevents). Custom theme data (name + palettes) needs a new storage key plus a validation scheme.appearance.ts, theindex.htmlbootstrap, and thethemes.cssscopes) must accommodate dynamic entries; also consider multi-user/multi-machine semantics and how curated themes stay editable-but-resettable.data-themelocally per card; an editor preview can reuse that mechanism with the edited palette applied inline.themes.cssare authoring guidance; whether the editor enforces or merely reports contrast is a design decision.