97 lines
3.9 KiB
Markdown
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
|