WFDASH-WEBAPP/docs/theme-preview/README.md
SJCoetz bb4233a9f2 feat(dashboard): add multi-theme system and supporting improvements
Add complete multi-theme dashboard system including:
- 8 selectable themes with persistent per-browser preferences
- Theme bootstrap script to prevent FOUC on page load
- 12+ reusable chart components (area, bar, donut, radial gauge, etc)
- Custom checkbox UI component
- New settings page for appearance customization

Other changes:
- Optimize supplier map API payload with getMappableSuppliers helper
- Add aria-label attributes to back buttons on detail pages for better accessibility
- Update next.config.ts to include new local development IP 192.168.1.128
- Update site manifest with all new favicon and PWA assets
- Fix styling and layout spacing in rewrite quote modal
2026-06-26 17:59:40 +02:00

3.9 KiB

WFDASH — Multi-theme system

Ship 8 selectable themes (5 standard + 3 standout) with a warm/dark mode toggle. Theme selection persists per browser. No FOUC on reload.

File map

src/
├── lib/
│   ├── themes.ts                 # 8-theme registry (id, label, fonts, swatches)
│   └── use-theme-id.ts           # Client hook to read/write active theme
├── styles/
│   └── themes.css                # OKLCH token blocks for all 8 themes (warm + dark)
├── components/
│   ├── theme-provider.tsx        # next-themes wrapper (manages warm/dark mode)
│   ├── theme-bootstrap.tsx       # Inline <head> script — prevents FOUC
│   ├── theme-picker.tsx          # Settings UI — mini preview cards
│   └── sidebar.tsx               # +Settings nav item
└── app/
    ├── layout.tsx                # +Inter, Space Grotesk, DM Sans, Playfair,
    │                             #  Lato, Nunito, Outfit, JetBrains Mono via next/font
    ├── globals.css               # Imports themes.css
    └── (app)/settings/page.tsx   # New Settings page with Appearance section

The 8 themes

# ID Category Fonts Radius Notes
1 crisp Standard Inter 10px Default. Calm, professional.
2 midnight Standard Space Grotesk + DM Sans 12px Dark control-room.
3 editorial Standard Playfair Display + Lato 6px Warm cream + serif.
4 bento Standard Inter 18px Tinted tiles, big radius.
5 field-ops Standard Inter 8px Dense, navy sidebar, gold CTA.
6 terminal Standout JetBrains Mono 0px Green CRT, scanlines, mono.
7 blueprint Standout JetBrains Mono + Inter 2px White-on-blue, grid, technical.
8 confetti Standout Outfit + Nunito 20px Pastel, playful, oversized radius.

How it works

Theme identity = data-theme="<id>" on <html>

  • Set by theme-bootstrap.tsx before hydration (prevents flash)
  • Updated by useThemeId() hook when user picks a new theme
  • Persisted to localStorage["wfdash-theme"]

Mode (warm/dark) = .warm or .dark class on <html>

  • Managed by next-themes (already wired in theme-provider.tsx)
  • Persisted to localStorage["wfdash-mode"]
  • Independent from theme — you can mix any theme with any mode

shadcn/ui adapts automatically because all components read from CSS vars (--background, --primary, --accent, --radius, etc.) that themes.css overrides per theme.

How to add a new theme

  1. Add an entry to THEMES in src/lib/themes.ts
  2. Add a [data-theme="<id>"] block in src/styles/themes.css (copy an existing theme and adjust tokens). Add .dark variant if it works in dark mode.
  3. Add radiusMap[<id>] in theme-bootstrap.tsx
  4. Done — picker picks it up automatically

How to ship a font swap

Each theme has fontHeading + fontBody pointing at a CSS var. Add the font to src/app/layout.tsx via next/font/google and register its .variable. Then add the binding in src/styles/themes.css:

[data-theme="<id>"] {
  --font-heading: var(--font-<your-font>);
  --font-body: var(--font-<your-font>);
}

The body picks up var(--font-body) automatically; font-heading-theme class applies heading font where you need it.

Standout theme caveats

The 3 standout themes override default component styling:

  • Terminal — sharp 0px radius, uppercase headings, mono everywhere, scanline body overlay
  • Blueprint — sharp 2px radius, uppercase headings, grid overlay, transparent inputs
  • Confetti — 20px radius (capped — shadcn's --radius-4xl calc clamps at ~1.6rem)

All three are still built on the brand palette (#4C96D7 / #F2AF58 / #E94E49) but applied in different ways. They're tested against the standard shadcn components but may render slightly differently from the 5 standard themes — by design.