Skip to content

2.2 color harmony

wiki[bot] edited this page Apr 27, 2026 · 1 revision

2.2 Color Harmony & Theming

Design Philosophy

The dashboard implements a multi-theme dark-mode system inspired by the visual palettes of contemporary video games—each theme evokes a distinct atmospheric mood while preserving functional readability. Rather than simple light/dark toggles, the system uses CSS custom property overrides to establish semantic color mappings that propagate through Tailwind CSS v4's @theme directive. This architecture decouples color values from component logic, allowing instantaneous theme switching without JavaScript-driven style computation or class list mutation.

Theme Taxonomy

Nine themes are available, each named after a video game that informed its color palette:

Theme ID Display Name Primary Accent Atmosphere
souls Dark Souls #cd853f (warm bronze) Gothic decay, ash, and bonfire light
diablo Diablo #c0392b (deep crimson) Demonic hellfire and blood-red shadows
gothic Gothic #4a6741 (moss green) Deep forest and obsidian stone
cyberpunk Cyberpunk 2077 #1abc9c (neon cyan) Synthwave neon and tech-noir
stellar Stellar Blade #e84393 (electric magenta) Sci-fi futurism and chrome
ghostwire Ghostwire Tokyo #1dd1a1 (spirit teal) Supernatural ether and urban mysticism
deathspace Dead Space #8e44ad (necrotic purple) Industrial decay and cosmic horror
nioh Nioh #e74c3c (sanguine red) Feudal Japan and supernatural corruption
pragmata Pragmata #4a6de5 (cobalt steel) Lunar station, cold and precise

Theme Architecture

CSS Custom Property Override

Each theme is a self-contained CSS file that overrides variables on :root[data-theme='{id}']:

/* themes/souls.css (excerpt) */
:root[data-theme='souls'] {
  --color-accent-primary: #e6a23c;
  --color-accent-secondary: #8b6914;
  --color-glow: #b8860b;

  --color-status-success: #7cb342;
  --color-status-warning: #f0a030;
  --color-status-error: #e74c3c;
  --color-status-info: #5dade2;

  --color-tab-rest: #cd853f;
  --color-tab-mcp: #c19665;
  --color-tab-debug: #d4a574;
  --color-tab-preprocessing: #e6a23c;

  --color-harmony-1: #f4d03f;
  --color-harmony-2: #cd853f;
  --color-harmony-3: #5d6d7e;
  --color-harmony-4: #5d4e37;

  --color-bg-primary: #0d0c0a;
  --color-bg-secondary: #1a1814;
  --color-bg-tertiary: #252018;
  --color-fg-primary: #e8dcc8;
  --color-fg-secondary: #c9b99a;
  --color-fg-muted: #9a8b75;
  --color-divider: #3d3228;
}

Global Color Registry

The colors.css file defines a semantic color system accessible via Tailwind v4's @theme block:

@theme {
  /* Fonts */
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
  --font-sans: 'Inter', system-ui, sans-serif;

  /* Semantic Tokens */
  --color-status-success: #00cc7f;
  --color-status-warning: #ff9933;
  --color-status-error: #ff4466;
  --color-status-info: #00cccc;

  --color-method-get: #00cc7f;
  --color-method-post: #00aadd;
  --color-method-put: #ddaa00;
  --color-method-delete: #dd4466;
  --color-method-patch: #aa44dd;

  --color-bg-primary: #0a0a0f;
  --color-bg-secondary: #12121a;
  --color-bg-tertiary: #1a1a2e;
  --color-fg-primary: #e8e8e8;
  --color-fg-secondary: #b8b8c8;
  --color-fg-muted: #9090a0;

  --color-accent-primary: #ff8c00;
  --color-accent-secondary: #4a3728;

  /* Harmony Colors */
  --color-harmony-1: #ffd700;
  --color-harmony-2: #9932cc;
  --color-harmony-3: #ff8c00;
  --color-harmony-4: #5f9ea0;

  /* Backward Compatibility */
  --color-primary: var(--color-bg-primary);
  --color-secondary: var(--color-bg-secondary);
  --color-brand: var(--color-accent-primary);
  --color-gold: var(--color-harmony-1);

  /* Tab Semantic Mapping */
  --color-tab-primary: var(--color-harmony-1);
  --color-tab-secondary: var(--color-harmony-2);
  --color-tab-tertiary: var(--color-harmony-3);

  /* Semantic Harmony Roles */
  --color-accent-hover: var(--color-harmony-2);
  --color-accent-active: var(--color-harmony-1);
  --color-accent-border: var(--color-harmony-3);
  --color-accent-muted: var(--color-harmony-4);
  --color-accent-glow: var(--color-harmony-1);
}

Theme Activation

The ThemeStore applies themes by setting a data-theme attribute on document.documentElement:

// stores/theme.ts
export const useThemeStore = defineStore('theme', () => {
  const currentTheme = ref<ThemeName>('souls');

  function applyTheme(theme: ThemeName) {
    document.documentElement.setAttribute('data-theme', theme);
  }

  watch(currentTheme, (newTheme) => {
    localStorage.setItem('theme', newTheme);
    applyTheme(newTheme);
  });

  function initTheme() {
    const saved = localStorage.getItem('theme') as ThemeName | null;
    if (saved && validThemes.includes(saved)) currentTheme.value = saved;
    applyTheme(currentTheme.value);
  }

  return { currentTheme, themeColors, darkThemes, darkThemes2, applyTheme, initTheme };
});

Theme state is persisted to localStorage and restored on application mount, ensuring consistency across sessions.

Theme Grouping

Themes are visually grouped in the AppThemeSwitcher component:

  • darkThemes: souls, diablo, gothic, cyberpunk, stellar, pragmata
  • darkThemes2: ghostwire, deathspace, nioh

This grouping controls layout spacing in the theme switcher sidebar and allows future extension (e.g., light theme categories) without UI refactoring.

Color Harmony System

Each theme defines four harmony accent colors (--color-harmony-1 through --color-harmony-4) that serve semantic roles across the interface. These roles abstract away raw color values, enabling theme-agnostic component styling:

Semantic Role CSS Variable Default Mapping Usage
Primary Accent --color-harmony-1 #ffd700 (gold) Active states, primary buttons, highlights
Secondary Accent --color-harmony-2 #9932cc (purple) Hover states, secondary highlights
Border/Divider --color-harmony-3 #ff8c00 (orange) Subtle separation elements
Muted/Disabled --color-harmony-4 #5f9ea0 (teal) Disabled controls, secondary text

The Cyberpunk theme demonstrates a cool-analogous harmony with complementary accents:

/* themes/cyberpunk.css */
:root[data-theme='cyberpunk'] {
  --color-accent-primary: #1abc9c;       /* primary: cyan */
  --color-accent-secondary: #16a085;
  --color-glow: #148f77;

  --color-harmony-1: #e84393;            /* complementary: magenta */
  --color-harmony-2: #f1c40f;            /* complementary: gold */
  --color-harmony-3: #8e44ad;            /* analogous: purple */
  --color-harmony-4: #3498db;            /* analogous: sky blue */
}

Tab Color Mapping

Each theme assigns a distinct color to functional tabs, creating visual differentiation without relying solely on iconography:

Tab Default Color Souls Theme Cyberpunk Theme
REST (tab-rest) #ff8c00 #cd853f (bronze) #1abc9c (cyan)
MCP (tab-mcp) #ffd700 #c19665 (light bronze) #1dd1a1 (teal)
Debug (tab-debug) #00cc7f #d4a574 (tan) #20e5b5 (aquamarine)
Preprocessing (tab-preprocessing) #ff6347 #e6a23c (amber) #23f9ca (mint)

AppThemeSwitcher Component

The theme switcher is a fixed-position sidebar rendered on the right edge of the viewport. It uses dynamic gradient backgrounds and text-shadow effects to preview each theme:

<template>
  <div class="fixed right-0 top-1/2 -translate-y-1/2 z-50 flex flex-col gap-0.5 pr-0 w-40 pointer-events-none font-mono">
    <template v-for="name in darkThemes" :key="name">
      <button
        class="group relative h-10 w-full flex items-center justify-end pointer-events-auto"
        @click="emit('change', name)"
      >
        <div
          class="w-28 h-full flex items-center justify-end pr-2 transition-all duration-300"
          :style="{
            background: currentTheme === name
              ? `linear-gradient(to left, ${themeColors[name].primary}88, ${themeColors[name].primary}20 30%, transparent)`
              : `linear-gradient(to left, ${themeColors[name].primary}15, transparent 60%)`
          }"
        >
          <span
            class="text-[10px] font-bold uppercase tracking-wider transition-all duration-300"
            :class="currentTheme === name ? 'text-fg-primary' : 'text-fg-muted/20 group-hover:text-fg-muted/80'"
            :style="currentTheme !== name ? { textShadow: `0 0 8px ${themeColors[name].primary}40` } : {}"
          >
            {{ themeColors[name].name }}
          </span>
        </div>
      </button>
    </template>
  </div>
</template>

The component uses CSS viewport-unit positioning (fixed right-0 top-1/2) to remain accessible without consuming main-content layout space.

Toast Color Integration

Theme colors propagate to the Toastification system via CSS overrides:

:root[data-theme='souls'] {
  --toastify-color-dark: #1a1814;
  --toastify-color-success: #7cb342;
  --toastify-color-warning: #f0a030;
  --toastify-color-error: #e74c3c;
  --toastify-color-info: #5dade2;
}

This ensures toast notifications harmonize with the active theme rather than falling back to generic light/dark defaults.

Semantic Status Colors

Status colors are mapped per theme to maintain contrast and cultural consistency:

Status Souls Theme Cyberpunk Theme Pragmata Theme
Success #7cb342 (moss) #2ecc71 (neon green) #2a9d8f (seafoam)
Warning #f0a030 (ember) #f1c40f (electric yellow) #e9c46a (brass)
Error #e74c3c (blood) #e74c3c (alert red) #e07a5f (rust)
Info #5dade2 (frost) #3498db (ice blue) #a8dadc (moonlight)

Method Colors

HTTP method badges are color-coded consistently across themes, ensuring API operation visibility:

Method Default Hex Tailwind Class
GET #00cc7f text-method-get
POST #00aadd text-method-post
PUT #ddaa00 text-method-put
DELETE #dd4466 text-method-delete
PATCH #aa44dd text-method-patch
System #888899 text-method-system

Contrast and Accessibility

All foreground text colors (--color-fg-primary, --color-fg-secondary) are calibrated against their respective background colors to maintain a minimum WCAG 2.1 AA contrast ratio of 4.5:1 for normal text. The theme system is intentionally dark-mode only to reduce eye strain during prolonged debugging sessions and to preserve the atmospheric aesthetic.

Related Documentation

Clone this wiki locally