HomeDocsTheme Engine

Theme Engine

CSS variable-based theming with built-in presets, live editing, and .soundie-theme file format.

Overview

The Soundie theme engine sets CSS custom properties on document.documentElement at startup and whenever the theme changes. Every color is stored as an RGB triplet (e.g. 139 92 246) so you can compose them with arbitrary opacity using rgb(var(--color-accent) / 0.15).

Themes are defined as .soundie-theme JSON files — a superset of a standard JSON schema that includes colors, glass settings, typography, layout dimensions, and motion speed.

Built-in Presets

Dark

Deep charcoal + violet

Midnight

Navy blue + indigo

Aurora

Dark teal + northern lights

Light

Crisp white + violet

Sunset

Warm amber + coral

Neon

Electric lime + cyberpunk

Rose

Deep rose + crimson

Ocean

Electric cyan + navy

CSS Variables Reference

VariableDescription
--color-surfaceBase background color (RGB triplet)
--color-surface-raisedSlightly elevated surfaces — cards, panels
--color-surface-overlayModal, dropdown, tooltip backgrounds
--color-surface-sunkenInset areas — sidebar, player bar
--color-accentPrimary brand color (RGB triplet)
--color-accent-hoverAccent on hover
--color-accent-activeAccent on press/active
--color-text-primaryMain text color
--color-text-secondarySubdued labels
--color-text-tertiaryFaint hints, placeholders
--color-text-disabledDisabled state text
--color-border-subtleHairline dividers
--color-borderDefault border
--color-border-strongEmphasized border, focus ring
--color-successSuccess / live indicators
--color-warningWarnings
--color-errorErrors and destructive actions
--color-player-bgPlayer bar background
--color-player-waveformWaveform track (unplayed)
--color-player-waveform-playedWaveform played portion
--glass-blur-radiusBackdrop blur in px
--glass-transparencySurface transparency 0–1
--glass-border-opacityBorder visibility 0–1
--glass-frost-intensityFrost/noise overlay intensity
--font-sansUI font family stack
--font-monoMonospace font stack
--font-displayDisplay / heading font
--layout-sidebar-widthSidebar width in px
--layout-player-heightPlayer bar height in px
--motion-speedAnimation speed multiplier

Usage in CSS / Tailwind

usage.css
css
1/* Use accent with opacity */
2.my-button {
3 background: rgb(var(--color-accent) / class="token-number">0.15);
4 color: rgb(var(--color-accent));
5 border: 1px solid rgb(var(--color-accent) / class="token-number">0.3);
6}
7
8/* Glass panel */
9.panel {
10 background: rgb(var(--color-surface-raised) / var(--glass-transparency));
11 backdrop-filter: blur(var(--glass-blur-radius));
12 border: 1px solid rgb(class="token-number">255 class="token-number">255 class="token-number">255 / var(--glass-border-opacity));
13}
14
15/* Responsive to theme changes automatically */
16[data-theme="light"] .panel {
17 /* No overrides needed — CSS vars update globally */
18}
usage-in-tailwind.tsx
ts
1// In Tailwind, use arbitrary values with CSS vars
2<div className="bg-[rgb(var(--color-surface-raised))]
3 border-[rgb(var(--color-border))]
4 text-[rgb(var(--color-text-primary))]">
5
6 <button className="bg-[rgb(var(--color-accent)/class="token-number">0.15)]
7 text-[rgb(var(--color-accent))]
8 hover:bg-[rgb(var(--color-accent)/class="token-number">0.25)]">
9 Accent Button
10 </button>
11</div>

.soundie-theme File Format

my-theme.soundie-theme
json
1{
2 class="token-prop">"$schema": "https://spotx.app/theme-schema/v1",
3 class="token-prop">"id": class="token-prop">"550e8400-e29b-41d4-a716-class="token-number">446655440000",
4 class="token-prop">"name": class="token-prop">"Cyberpunk class="token-number">2077",
5 class="token-prop">"description": class="token-prop">"Night City vibes with electric yellow on black.",
6 class="token-prop">"author": class="token-prop">"you",
7 class="token-prop">"version": class="token-prop">"class="token-number">1.0.class="token-number">0",
8 class="token-prop">"base": class="token-prop">"dark",
9 class="token-prop">"isBuiltin": class="token-keyword">false,
10 class="token-prop">"colors": {
11 class="token-prop">"surface": { class="token-prop">"base": "#0A0A0A", "raised": "#141414", "overlay": "#1E1E1E", "sunken": "#050505" },
12 class="token-prop">"accent": { class="token-prop">"primary": "#F0E614", "primaryHover": "#F5EE50", "primaryActive": "#C4BC06", "secondary": "#FF2D78" },
13 class="token-prop">"text": { class="token-prop">"primary": "#F0F0E0", "secondary": "#A0A080", "tertiary": "#606050", "disabled": "#303025", "inverse": "#0A0A0A" },
14 class="token-prop">"border": { class="token-prop">"subtle": "#1A1A10", "default": "#2A2A1A", "strong": "#404030" },
15 class="token-prop">"status": { class="token-prop">"success": "#00FF41", "warning": "#F0E614", "error": "#FF2D78", "info": "#00BFFF" },
16 class="token-prop">"player": { class="token-prop">"background": "#080808", "waveform": "#1E1E10", "waveformPlayed": "#F0E614" }
17 },
18 class="token-prop">"glass": { class="token-prop">"blurRadius": class="token-number">24, class="token-prop">"transparency": class="token-number">0.65, class="token-prop">"borderOpacity": class="token-number">0.08, class="token-prop">"frostIntensity": class="token-number">0.4, class="token-prop">"noiseAmount": class="token-number">0.05 },
19 class="token-prop">"typography": { class="token-prop">"fontFamilyUi": : class="token-string">"\class="token-prop">"Share Tech Mono\", monospace", class="token-prop">"fontFamilyMono": : class="token-string">"\class="token-prop">"Share Tech Mono\", monospace", class="token-prop">"fontFamilyDisplay": : class="token-string">"\class="token-prop">"Share Tech Mono\", monospace" },
20 class="token-prop">"layout": { class="token-prop">"sidebarWidthPx": class="token-number">220, class="token-prop">"playerHeightPx": class="token-number">88, class="token-prop">"panelWidthPx": class="token-number">300 },
21 class="token-prop">"motion": { class="token-prop">"speedMultiplier": class="token-number">0.8 },
22 class="token-prop">"vibrancy": { class="token-prop">"macOs": class="token-prop">"HudWindow", class="token-prop">"windows": : class="token-string">"acrylic" }
23}

ThemeEngine API

ThemeEngine.ts
ts
1import { ThemeEngine } from "@soundie/theme-engine";
2
3// Apply a full theme to the document root
4ThemeEngine.apply(theme);
5
6// Apply only colors (e.g. after a color picker change)
7ThemeEngine.applyColors(theme.colors);
8
9// Apply only glass settings
10ThemeEngine.applyGlass(theme.glass);
11
12// Get the full CSS property map without applying
13const props = ThemeEngine.buildPropertyMap(theme);
14// { "--color-accent": "139 92 246", ... }
useTheme.ts
ts
1import { useTheme } from "@soundie/theme-engine";
2
3function MyComponent() {
4 const {
5 theme, // Current ThemeDefinition
6 allThemes, // All themes (presets + custom)
7 setTheme, // (id: string) => Promise<void>
8 importTheme, // (json: string) => Promise<ThemeDefinition>
9 exportTheme, // (id: string) => string (JSON)
10 deleteTheme, // (id: string) => Promise<void>
11 updateColors, // (patch: Partial<ColorPalette>) => Promise<void>
12 updateGlass, // (patch: Partial<GlassConfig>) => Promise<void>
13 updateTypography,// (patch: Partial<TypographyConfig>) => Promise<void>
14 updateLayout, // (patch: Partial<LayoutConfig>) => Promise<void>
15 } = useTheme();
16}