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).
: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:
- Open
packages/ui-kit/src/styles/tokens.css. - 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. - Add the kind to the
setThemeenum inpackages/extension-api/src/index.ts. - Add a
theme.<my-theme>palette action inpackages/extension-core/src/index.ts(icon: pick from lucide). - 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). - Add a segment button in
AppearanceSection.svelte. - 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.cssunder the[data-theme="cyberpunk"]::afterselector + the LCD text-shadow rules.