Skip to content

Theme switching support #449

@esengine

Description

@esengine

Background

Multiple users have asked for built-in theme switching. Right now we ship exactly one dark palette, hardcoded at module-eval time across two parallel token sets (src/cli/ui/theme.ts and src/cli/ui/theme/tokens.ts). Users on light terminals, high-contrast setups, or with strong palette preferences have no way to opt out short of forking.

Goals

  • Ship at least three first-party themes: dark (current default), light, and high-contrast.
  • Persist the selection across sessions in ~/.reasonix/config.json.
  • Switch live from inside the chat UI via /theme <name> and from the CLI via reasonix theme <name> / reasonix theme list.
  • Lay the groundwork for user-defined themes (JSON file dropped in ~/.reasonix/themes/) without committing to that surface in v1.

Non-goals (v1)

  • Per-card or per-component overrides.
  • Hot-reloading user theme files on disk change.
  • Light-mode redesign of cards that currently rely on dark-only contrast (tracked separately as visual polish once the plumbing lands).

Proposal

1. Unify the token surface. Collapse theme.ts and theme/tokens.ts into one canonical Theme shape (COLOR, SURFACE, FG, GRADIENT, CARD, GLYPH). The two-file split is a historical accident and blocks a single-source swap. This is a precondition, not a feature.

2. Theme registry. Move the current constants into themes/dark.ts, add themes/light.ts and themes/high-contrast.ts, and expose them through themes/index.ts keyed by name. The active theme is resolved once at startup from config and held in a small context.

3. Runtime access pattern. Replace direct import { COLOR } from \"./theme\" reads with a useTheme() hook (and a non-React getTheme() for plain modules). Components re-render on swap via context. Migration is mechanical across the ~57 call sites; it can land incrementally behind a default-resolved theme so partial migrations don't break.

4. Persistence. Add theme?: string to ReasonixConfig. Unknown names fall back to dark with a one-line warn on the toast rail.

5. User-facing surfaces.

  • `/theme` — opens the existing `Select` picker, previews swatches, commits on Enter.
  • `/theme ` — direct switch.
  • `reasonix theme list` / `reasonix theme set ` — for users who want to script it or set a default before first launch.

6. Glyph parity. `GLYPH` stays theme-independent in v1. Themes only remap colors. Any glyph variation belongs in a separate compat axis (emoji-safe, ASCII-only) we already need for Windows CMD and CI logs.

Open questions

  • Ship `solarized-dark` / `solarized-light` first-party, or wait for the user-theme file format and let the community PR them?
  • Should the welcome banner gradient (`GRADIENT`) participate in theming, or stay brand-locked? Leaning brand-locked.
  • Config key naming: `theme` vs `appearance` vs `colors`. Going with `theme` unless someone has a reason.

Rollout

  1. Token unification PR (no behavior change).
  2. Hook + context PR; migrate call sites in two or three batches.
  3. Add `light` + `high-contrast` themes + `/theme` slash + CLI subcommand.
  4. Document in README once the surface settles.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttrackingTracking issue / umbrella for a multi-PR effort

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions