# Themes


FaustWave ships four theme modes: **Dark**, **Light**, **Cyberpunk**, **Auto**. Switch via Settings → Appearance, via the Command Palette (`Theme: Dark` / `Theme: Light` / `Theme: Cyberpunk` / `Theme: System`), or via the `theme.set` MCP action.

## The four modes

| Mode | When to pick it |
|---|---|
| **Dark** | Default. High contrast, reduced eye strain in low light. |
| **Light** | Bright environments, daylight, screens you share. |
| **Cyberpunk** | Neon-on-near-black, LED-panel aesthetic. For vibe. |
| **Auto** | Follows your OS appearance setting. Switches at sunset if your OS is configured to. |

The chosen mode persists per-install (not per-project) — it's a user preference, not a project asset.

## How the theme system works

Every theme is a block of CSS custom properties under a single `:root[data-theme="..."]` selector in `packages/ui-kit/src/styles/tokens.css`. The `setTheme()` function sets `<html data-theme="...">`; the rest of the codebase reads the tokens via `var(--token-name)`.

```css
:root[data-theme="dark"] {
  --bg: #0b0e14;
  --text: #e2e8f0;
  --cyan: #00d4f5;
  /* ... ~80 more tokens */
}
:root[data-theme="light"] { /* ... */ }
:root[data-theme="cyberpunk"] { /* ... */ }
```

This means **the same component code runs in every theme** — only the token values differ. Build new components against the token names; they'll automatically theme.

## The token taxonomy

Roughly 80 tokens per theme, grouped:

| Group | Tokens |
|---|---|
| **Backgrounds** | `--bg`, `--bg-2`, `--bg-3`, `--panel`, `--panel-2`, `--card`, `--card-2` |
| **Borders** | `--border`, `--border-2`, `--border-3`, `--card-border` |
| **Text tiers** | `--text` (primary), `--text-2` (muted), `--text-3` (dimmed), `--text-4` (placeholder) |
| **Shadows** | `--shadow-lg`, `--shadow-md`, `--shadow-sm` |
| **Accent colors** | 8 hues: `--cyan`, `--purple`, `--pink`, `--orange`, `--green`, `--yellow`, `--red`, `--blue` |
| **Hover variants** | `--red-hover`; the teal's is a role, `--action-hover` |
| **Accent backgrounds** | 5 alpha tiers × 8 hues: `--accent-bg-cyan-subtle`, `-15`, etc. |
| **Accent borders** | 3 alpha tiers × 8 hues: `--accent-border-cyan-40`, `-50`, etc. |
| **Glow effects** | One per hue: `--glow-cyan`, etc. |
| **Wave + node-editor** | `--wave-stroke`, `--wave-fill`, `--wave-grid`, `--node-bg`, `--node-edge`, `--node-port` |
| **Sequencer track palette** | `--seq-track-1` through `--seq-track-6` (per-track colour-coded) |
| **Utility overlays** | `--overlay-dark`, `--overlay-dark-35`, etc. |

## Cyberpunk-only effects

The Cyberpunk theme adds an overlay layer on top of the regular token block — purely visual atmosphere:

- **LED dot-matrix overlay** — a 4 px-tile cyan dot grid via `[data-theme="cyberpunk"]::after`, `mix-blend-mode: screen`. Reads as a faint LED panel substrate at typical viewing distance.
- **Magenta-edge vignette** — soft radial gradient pulling the eye toward centre.
- **Phosphor text-shadow** on the brand title + the Transport Widget's LCD readouts — tight 1 px core + 4–6 px halo, reads as a discrete pixel emitter.

These effects are scoped via `[data-theme="cyberpunk"]` selectors — Dark and Light don't see them.

## Building a fourth theme

To add a custom theme:

1. Open `packages/ui-kit/src/styles/tokens.css`.
2. Add a new block: `:root[data-theme="my-theme"] { /* full token set */ }`. Copy the Dark or Light block as a starting point + retune hex values.
3. Add the kind to the `setTheme` enum in `packages/extension-api/src/index.ts`.
4. Add a `theme.<my-theme>` palette action in `packages/extension-core/src/index.ts` (icon: pick from lucide).
5. Add the kind to the persistence allow-list in `packages/app/src/lib/persistence.ts` (the hydration validator that normalises unknown themes back to dark).
6. Add a segment button in `AppearanceSection.svelte`.
7. Add a Theme-Indicator preview in `CommandPalette.svelte`.

About 6 small edits — none structural. The theme system was deliberately built to make adding a new theme a half-hour job.

## Theme persistence

The chosen theme persists across restarts via `app.json`. The hydration step also has a validator that normalises any unknown theme back to Dark — leftover from a previous theme experiment + a safety net against corrupted state.

## Where to go from here

- The actual token values per theme live in `packages/ui-kit/src/styles/tokens.css` — read it to see the exact hex / rgba per token per theme.
- The Cyberpunk overlay rules live in `packages/app/src/styles/global.css` under the `[data-theme="cyberpunk"]::after` selector + the LCD text-shadow rules.
