# 2.3. Theming & Color Identity 3F has a deliberately strong visual identity: the dashboard ships a catalogue of **10 game-inspired themes**, each a complete color harmony rather than a reskin. ## Theme catalogue | Key | Name | | -------------- | --------------- | | `gothic` | Gothic | | `pragmata` | Pragmata | | `souls` | Dark Souls | | `residentevil` | Resident Evil | | `cyberpunk` | Cyberpunk 2077 | | `baldursgate` | Baldur's Gate | | `stellar` | Stellar Blade | | `deathspace` | Dead Space | | `wuchang` | Wuchang | | `nioh` | Nioh | The default theme is **`stellar`** (`DEFAULT_THEME` in `stores/theme.ts`). ## Mechanics - Themes are applied declaratively: `document.documentElement` carries **`data-theme=""`** and a **`data-theme-mode`** (light/dark) attribute. All colors resolve from CSS design tokens scoped to these attributes — components never hardcode palette values. - The `theme` Pinia store (`src/stores/theme.ts`) owns the catalogue, persistence, and application: `initTheme()` restores the stored choice on boot (before first paint, from `App.vue`'s `onBeforeMount`), `applyTheme()` switches at runtime with no reload. - The selector UI is `components/app/app-theme-selector/AppThemeSelector.vue` in the app header. ## Tokens before utilities Styling convention across the codebase: **semantic tokens first, Tailwind v4 for layout/spacing**. Theme palettes define token values; components consume tokens, so a new theme is a new token set, not a code change. Scoped CSS holds component-specific rules; `@apply` is avoided in favor of plain CSS against tokens. ## Adding a theme 1. Pick a harmony (backgrounds, surfaces, accents, text, states) and add the key to the catalogue in `stores/theme.ts`. 2. Define its token set under `[data-theme=""]` (and, where it differs, `[data-theme-mode]`). 3. Add/refresh a story for `AppThemeSelector` so the visual state is covered. That is the entire cost of a new identity — tokens do the rest.