Skip to main content

Theming

RedactedWorld ships with three built-in visual themes. Users can switch themes at any time, and their preference is persisted both locally and server-side.

Available Themes

ThemeBackgroundAccentFontDescription
Dark Hacker#0a0a0a#00ff41JetBrains MonoTerminal-inspired dark theme with green-on-black aesthetics. Designed for users who prefer a classic hacker terminal look. This is the default theme.
Clean Professional#fafafa#2563ebInterLight theme with blue accents. Optimized for readability in bright environments and professional presentations.
Hybrid#121212#a855f7InterDark theme with purple accents. A modern dark mode that blends the readability of the professional theme with a dark background.

Theme Preview

Dark Hacker

Background:  #0a0a0a  (near black)
Surface: #1a1a1a (dark gray)
Border: #2a2a2a (subtle gray)
Text: #e0e0e0 (light gray)
Accent: #00ff41 (terminal green)
Error: #ff4444 (red)
Warning: #ffaa00 (amber)
Font: JetBrains Mono, monospace

Clean Professional

Background:  #fafafa  (off-white)
Surface: #ffffff (white)
Border: #e2e8f0 (light slate)
Text: #1e293b (dark slate)
Accent: #2563eb (blue-600)
Error: #dc2626 (red-600)
Warning: #d97706 (amber-600)
Font: Inter, system-ui, sans-serif

Hybrid

Background:  #121212  (dark)
Surface: #1e1e1e (dark gray)
Border: #333333 (medium gray)
Text: #e0e0e0 (light gray)
Accent: #a855f7 (purple-500)
Error: #ef4444 (red-500)
Warning: #f59e0b (amber-500)
Font: Inter, system-ui, sans-serif

Implementation

CSS Custom Properties

Themes are implemented using CSS custom properties (variables) defined at the :root level. Each theme overrides these variables through a class applied to the <body> element.

/* Base variables (Dark Hacker - default) */
:root {
--rw-bg: #0a0a0a;
--rw-surface: #1a1a1a;
--rw-border: #2a2a2a;
--rw-text: #e0e0e0;
--rw-text-muted: #888888;
--rw-accent: #00ff41;
--rw-accent-hover: #00cc33;
--rw-error: #ff4444;
--rw-warning: #ffaa00;
--rw-font-body: 'JetBrains Mono', monospace;
--rw-font-heading: 'JetBrains Mono', monospace;
--rw-radius: 4px;
}

/* Clean Professional theme */
body.theme-professional {
--rw-bg: #fafafa;
--rw-surface: #ffffff;
--rw-border: #e2e8f0;
--rw-text: #1e293b;
--rw-text-muted: #64748b;
--rw-accent: #2563eb;
--rw-accent-hover: #1d4ed8;
--rw-error: #dc2626;
--rw-warning: #d97706;
--rw-font-body: 'Inter', system-ui, sans-serif;
--rw-font-heading: 'Inter', system-ui, sans-serif;
--rw-radius: 8px;
}

/* Hybrid theme */
body.theme-hybrid {
--rw-bg: #121212;
--rw-surface: #1e1e1e;
--rw-border: #333333;
--rw-text: #e0e0e0;
--rw-text-muted: #999999;
--rw-accent: #a855f7;
--rw-accent-hover: #9333ea;
--rw-error: #ef4444;
--rw-warning: #f59e0b;
--rw-font-body: 'Inter', system-ui, sans-serif;
--rw-font-heading: 'Inter', system-ui, sans-serif;
--rw-radius: 8px;
}

Theme Switching

The Angular frontend applies theme classes to the <body> element:

// theme.service.ts
@Injectable({ providedIn: 'root' })
export class ThemeService {
private readonly STORAGE_KEY = 'rw-theme';
private readonly DEFAULT_THEME = 'dark-hacker';

setTheme(theme: 'dark-hacker' | 'professional' | 'hybrid'): void {
// Remove existing theme classes
document.body.classList.remove(
'theme-dark-hacker',
'theme-professional',
'theme-hybrid'
);

// Apply new theme class (dark-hacker is the default, no class needed)
if (theme !== 'dark-hacker') {
document.body.classList.add(`theme-${theme}`);
}

// Persist locally
localStorage.setItem(this.STORAGE_KEY, theme);

// Persist to server
this.userService.updatePreferences({ theme }).subscribe();
}
}

Persistence Strategy

Theme preference is stored in two locations for optimal UX:

  1. localStorage -- Provides instant theme application on page load, before any API call completes. This prevents the "flash of wrong theme" problem.

  2. PostgreSQL (via user-service) -- Ensures the preference follows the user across devices. When a user logs in on a new device, the API response includes their theme preference, which is then written to localStorage.

On page load, the resolution order is:

  1. Check localStorage for a saved theme.
  2. If not found, use the API response from the user profile.
  3. If neither exists, fall back to dark-hacker.