Files
scalesApp/.claude/plans/ui-theming.md
T

97 lines
3.9 KiB
Markdown

# UI Theming — switchable themes + "cockpit" dark skin
## Context
The current UI is functional but flat and generic. The user wants to try a
different look (reference: a dark "cockpit" dashboard mockup — deep navy
surfaces, cyan/amber accents, glow on the live reading, photo banner) **without
losing the ability to switch back**.
Today there is no theming layer at all: **0 CSS custom properties, ~430
hardcoded colours across 16 CSS files**. So the switch has to be built before
any new skin can exist.
Key finding that reduces scope: `Main.jsx` **already has the card structure**
the mockup shows — `.weighing-section` containing `.weight-card` elements for
Tare / Gross / Scale / Net, each with header, value and meta. No restructuring
of the detail panel is needed; this is a styling job.
## Approach
Token layer + `data-theme` attribute on `<html>`. Two themes:
- `classic` — reproduces today's look exactly (the safe default)
- `cockpit` — the new dark skin
### 1. Token layer
New `frontend/src/styles/tokens.css`, imported once in `index.css`:
```css
:root, :root[data-theme="classic"] {
--bg, --surface, --surface-raised, --border,
--text, --text-muted, --accent, --accent-contrast,
--success, --warning, --danger,
--radius, --shadow-card, --glow-accent
}
:root[data-theme="cockpit"] { /* same names, dark values */ }
```
Token NAMES are semantic (`--surface`), never literal (`--navy`), so a third
theme is one block.
### 2. Convert existing CSS to var()
Mechanical replacement of hardcoded colours. `classic` values are lifted from
the current CSS so the default theme is **visually unchanged** — that is the
acceptance test.
### 3. Theme switch
- `ThemeContext` (mirrors the existing `AuthContext`/`NomenclatureContext`
pattern in `frontend/src/contexts/`)
- Sets `data-theme` on `document.documentElement`
- Persists to `localStorage`, wrapped in try/catch (private mode can throw)
- Toggle control in `Header.jsx`
- **Default: `classic`** — nothing changes for operators unless they opt in
### 4. Cockpit theme + banner
Dark palette, raised card surfaces, cyan accent, glow on the live reading,
monospace weight readouts. Banner strip behind the vehicle title in
`.vehicle-detail` — image asset TBD (CSS gradient placeholder until the user
supplies one). Banner is cockpit-only and must collapse on short viewports;
this may run on a weigh-station monitor.
## Staging
Deliberately two stages so a wrong palette is caught after 3 files, not 16.
**Stage 1** — tokens, ThemeContext, header toggle, and convert:
- `frontend/src/components/Main.css` (68 colours)
- `frontend/src/components/Header.css` (17)
- `frontend/src/components/ScaleMonitor/ScaleMonitor.css` (47)
- `frontend/src/index.css`, `frontend/src/App.css`
→ user reviews both themes on the main weighing screen.
**Stage 2** — remaining 13 files, once the direction is approved:
NomenclatureManager (79), NomenclatureManagementOverlay (54),
LicenseManager (45), ChangePasswordOverlay (22), ExtraCombinationsTable (20),
DataDisplay (19), Login (15), NomenclatureEntryForm (14),
NomenclatureDropdown (14), FormOverlay, ReportPreviewModal.
## Critical files
- `frontend/src/styles/tokens.css` (new)
- `frontend/src/contexts/ThemeContext.jsx` (new)
- `frontend/src/components/Header.jsx` — toggle control
- `frontend/src/App.jsx` — wrap in ThemeProvider
- `frontend/src/components/Main.css` — the main surface
Report editor CSS (`components/ReportEditor/`) is **out of scope**: the A4
designer renders a paper page that must stay white regardless of theme.
## Verification
1. `npx vitest run` — 201 tests still pass
2. Classic theme vs `git stash` screenshot: **visually identical**
3. Toggle switches instantly, no reload; survives refresh (localStorage)
4. Cockpit: check contrast on the live reading, balanced/unbalanced states,
and that the banner collapses on a short viewport
5. Real browser check of both themes on the main weighing screen