# WFDASH themes The current theme picker is **Settings → Appearance** (`/settings`). The historical `themes.html` mockup in this directory predates the current registry and is not a preview of the shipping palettes. `src/lib/themes.ts` lists the selectable themes. `src/styles/themes.css` is the single source for their colours; picker cards and the About swatches use those same CSS tokens rather than maintaining a second set of hex approximations. - `data-theme=""` selects a palette on ``. - `light` / `dark` classes select the mode through `next-themes`. - Choices persist per browser under `wfdash-theme` and `wfdash-mode`. - The default is **WFDASH + Light**. Old `warm` modes migrate to `light` and retired or invalid theme IDs fall back to WFDASH before hydration. - Standard palettes provide both light and dark surfaces. The palettes marked `category: "dark"` are intentionally dark in either mode. - Preview elements scope the same tokens through `.theme-preview[data-theme]`; their mode follows the mode selected in Settings. ## Adding or adjusting a palette 1. Add its metadata to `THEMES` in `src/lib/themes.ts`. Preserve existing IDs; `MiniMax` is intentionally case-sensitive for saved preferences. 2. Define its tokens in `src/styles/themes.css` using `:is(:root, .theme-preview)[data-theme=""]`. For a standard palette, also define a `.dark` variant with sufficient specificity to override its light surface. A bare `.dark` selector loses to the base palette selector. 3. Define background/foreground pairs, muted text, semantic status colours, input/focus, sidebar, radius and fonts. Shared chart and AI tokens have defaults in `globals.css`. AI purple must follow the actual surface: include any new natively dark palette in the dark AI selector there. Legacy `text-{warn,success,info}-foreground` utilities adapt to tinted/plain text and exact solid fill classes; new tinted badges should use the semantic text class directly. These custom text utilities replace the generic Tailwind aliases to avoid duplicate declarations. The inverse info background/border utilities remain available for selected-customer checkmark bubbles. 4. Keep business meaning consistent: success green, warnings amber/orange, destructive red, price checks blue, AI purple. Foreground tokens accompany solid fills; semantic text colours must also be readable on tinted badges. 5. Verify in the actual app. The login page and business chart series retain their intentional brand/data colours; chart surfaces use theme tokens. ## Verification ```sh npm run test:run -- src/components/theme-bootstrap.test.tsx npm run e2e -- e2e/themes.spec.ts ``` The browser matrix checks all registered themes in both modes for dark surfaces, text contrast, filled and tinted semantic pairs, primary button hover labels, focus-colour contrast and chart token coverage. It uses the shared CSS from the public login route, so this part requires no PocketBase fixture. For authenticated preview/persistence and dialog/mobile checks, supply `E2E_TEST_EMAIL` / `E2E_TEST_PASSWORD`, or a Playwright `E2E_STORAGE_STATE` file for the test origin. These checks skip when no browser fixture is supplied. Use isolated browser storage; never commit credentials or storage-state files.