docs(m10): T34 theme dark-mode feasibility spike findings
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
# T34 Spike: Theme Dark-Mode Feasibility Findings
|
||||
|
||||
**Date:** 2026-06-18
|
||||
**Task:** Verify whether the external `ZB.MOM.WW.Theme` shell can go dark via `data-bs-theme` CSS override.
|
||||
|
||||
---
|
||||
|
||||
## 1. CSS Load Order
|
||||
|
||||
Confirmed from `src/ZB.MOM.WW.ScadaBridge.Host/Components/App.razor`:
|
||||
|
||||
```
|
||||
bootstrap.min.css (line 8)
|
||||
bootstrap-icons.css (line 9)
|
||||
<ThemeHead /> (line 10) → injects theme.css then layout.css
|
||||
Host.styles.css (line 11)
|
||||
site.css (line 12) ← our override point
|
||||
```
|
||||
|
||||
`site.css` loads **after** theme.css and layout.css. A `[data-bs-theme="dark"]` block in site.css has sufficient specificity to shadow every `:root` token defined in theme.css, because `[attr]` selector specificity (0,1,0) beats `:root` (0,0,1) at equal cascade layer.
|
||||
|
||||
---
|
||||
|
||||
## 2. Token Inventory (theme.css `:root`)
|
||||
|
||||
All tokens defined in the `:root` block of `~/.nuget/packages/zb.mom.ww.theme/0.3.1/staticwebassets/css/theme.css`:
|
||||
|
||||
### Surfaces & ink
|
||||
| Token | Light value | Role |
|
||||
|---|---|---|
|
||||
| `--paper` | `#f4f4f1` | page background |
|
||||
| `--card` | `#ffffff` | raised surfaces, bars, table heads |
|
||||
| `--ink` | `#1b1d21` | primary text |
|
||||
| `--ink-soft` | `#5a6066` | secondary text, labels |
|
||||
| `--ink-faint` | `#8b9097` | tertiary text, captions |
|
||||
| `--rule` | `#e4e4df` | hairline borders, row dividers |
|
||||
| `--rule-strong` | `#d2d2cb` | emphasised hairlines, bar underline, pills |
|
||||
|
||||
### Accent
|
||||
| Token | Light value | Role |
|
||||
|---|---|---|
|
||||
| `--accent` | `#2f5fd0` | links, sort arrows, primary actions |
|
||||
| `--accent-deep` | `#1e3f99` | hover/pressed accent |
|
||||
|
||||
### Status — foreground
|
||||
| Token | Light value |
|
||||
|---|---|
|
||||
| `--ok` | `#2f9e44` |
|
||||
| `--warn` | `#e8920c` |
|
||||
| `--bad` | `#e03131` |
|
||||
| `--idle` | `#868e96` |
|
||||
|
||||
### Status — tinted backgrounds
|
||||
| Token | Light value |
|
||||
|---|---|
|
||||
| `--ok-bg` | `#e9f6ec` |
|
||||
| `--warn-bg` | `#fdf1dd` |
|
||||
| `--bad-bg` | `#fceaea` |
|
||||
| `--idle-bg` | `#eef0f2` |
|
||||
|
||||
### Status — borders
|
||||
| Token | Light value |
|
||||
|---|---|
|
||||
| `--ok-border` | `#c6e6cd` |
|
||||
| `--warn-border` | `#efd6a6` |
|
||||
| `--bad-border` | `#eec3c3` |
|
||||
| `--ok-border-soft` | `#bfe3c6` |
|
||||
| `--warn-border-soft` | `#f0d9ab` |
|
||||
| `--bad-border-soft` | `#f0c0c0` |
|
||||
|
||||
### Warning ink
|
||||
| Token | Light value |
|
||||
|---|---|
|
||||
| `--warn-ink` | `#b56a00` |
|
||||
| `--warn-ink-deep` | `#8a5a00` |
|
||||
|
||||
### Info tint
|
||||
| Token | Light value |
|
||||
|---|---|
|
||||
| `--info-bg` | `#e7ecfb` |
|
||||
| `--info-border` | `#cdd9f7` |
|
||||
|
||||
### Neutral surface washes
|
||||
| Token | Light value | Role |
|
||||
|---|---|---|
|
||||
| `--zebra-bg` | `#fbfbf9` | even-row / sticky-head fill |
|
||||
| `--hover-bg` | `#f3f6fd` | row / rail-link hover |
|
||||
| `--active-bg` | `#eef2fc` | active rail-link fill |
|
||||
|
||||
### Bootstrap overrides (derive from above)
|
||||
| Token | Value |
|
||||
|---|---|
|
||||
| `--bs-body-bg` | `var(--paper)` |
|
||||
| `--bs-body-color` | `var(--ink)` |
|
||||
| `--bs-body-font-family` | `var(--sans)` |
|
||||
| `--bs-body-font-size` | `0.9rem` |
|
||||
| `--bs-primary` | `var(--accent)` |
|
||||
| `--bs-border-color` | `var(--rule)` |
|
||||
| `--bs-emphasis-color` | `var(--ink)` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Rail Tokenization Audit (layout.css)
|
||||
|
||||
Every colour-bearing rule in the side-rail uses CSS custom properties. No hard-coded hex or rgb values appear anywhere in layout.css. Key rules:
|
||||
|
||||
**Rail background and border:**
|
||||
```css
|
||||
.side-rail {
|
||||
background: var(--card);
|
||||
border-right: 1px solid var(--rule-strong);
|
||||
}
|
||||
```
|
||||
|
||||
**Brand block:**
|
||||
```css
|
||||
.side-rail .brand {
|
||||
color: var(--ink);
|
||||
border-bottom: 1px solid var(--rule);
|
||||
}
|
||||
.side-rail .brand .mark { color: var(--accent); }
|
||||
```
|
||||
|
||||
**Section eyebrow / toggle:**
|
||||
```css
|
||||
.rail-eyebrow-toggle { color: var(--ink-faint); }
|
||||
.rail-eyebrow-toggle:hover { color: var(--ink); }
|
||||
.rail-section > summary::before { color: var(--ink-faint); }
|
||||
```
|
||||
|
||||
**Nav links (default / hover / active):**
|
||||
```css
|
||||
.rail-link { color: var(--ink-soft); }
|
||||
.rail-link:hover { background: var(--hover-bg); color: var(--ink); }
|
||||
.rail-link.active { background: var(--active-bg); border-left-color: var(--accent); color: var(--accent-deep); }
|
||||
```
|
||||
|
||||
**Rail foot / session block:**
|
||||
```css
|
||||
.rail-foot { border-top: 1px solid var(--rule); }
|
||||
.rail-btn { color: var(--ink-soft); background: var(--card); border: 1px solid var(--rule-strong); }
|
||||
.rail-btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.rail-roles { color: var(--ink-faint); }
|
||||
.rail-ico { color: var(--ink-faint); }
|
||||
```
|
||||
|
||||
**Login title:**
|
||||
```css
|
||||
.login-title { color: var(--ink); }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. VERDICT
|
||||
|
||||
**RAIL TOKENIZES → dark feasible by token override.**
|
||||
|
||||
Every rail background, text, and border colour in layout.css resolves through a CSS custom property from the token set. Overriding the token values under `[data-bs-theme="dark"]` in site.css (which loads after theme.css/layout.css) will fully recolour the side-rail, app chrome, panels, data tables, chips, and status elements — no Theme-package coordination required.
|
||||
|
||||
---
|
||||
|
||||
## 5. Concrete Dark Token Override List
|
||||
|
||||
Paste this entire block under `[data-bs-theme="dark"]` in site.css. Accent colour (`--accent`) is kept at `#4d7fe8` — a lighter shade of the brand blue that reads well on dark backgrounds while preserving brand identity.
|
||||
|
||||
```css
|
||||
[data-bs-theme="dark"] {
|
||||
/* Surfaces & ink */
|
||||
--paper: #1b1d21;
|
||||
--card: #25282d;
|
||||
--ink: #e6e8ec;
|
||||
--ink-soft: #9aa0aa;
|
||||
--ink-faint: #6b727c;
|
||||
--rule: #33373e;
|
||||
--rule-strong: #3a3f46;
|
||||
|
||||
/* Accent — lightened for contrast on dark */
|
||||
--accent: #4d7fe8;
|
||||
--accent-deep: #7aa0ef;
|
||||
|
||||
/* Status — foregrounds unchanged (inherently visible on dark) */
|
||||
--ok: #40b856;
|
||||
--warn: #f0a030;
|
||||
--bad: #f05050;
|
||||
--idle: #7a8290;
|
||||
|
||||
/* Status — tinted backgrounds (dark surface tints) */
|
||||
--ok-bg: #1a2e1e;
|
||||
--warn-bg: #2e2210;
|
||||
--bad-bg: #2e1616;
|
||||
--idle-bg: #25282d;
|
||||
|
||||
/* Status — borders on dark tints */
|
||||
--ok-border: #2a5030;
|
||||
--warn-border: #4a3510;
|
||||
--bad-border: #4a2020;
|
||||
--ok-border-soft: #264a2c;
|
||||
--warn-border-soft: #44300e;
|
||||
--bad-border-soft: #44201e;
|
||||
|
||||
/* Warning ink — brightened amber for dark contrast */
|
||||
--warn-ink: #d4891a;
|
||||
--warn-ink-deep: #b8741a;
|
||||
|
||||
/* Info tint */
|
||||
--info-bg: #1a2240;
|
||||
--info-border: #2a3660;
|
||||
|
||||
/* Neutral surface washes */
|
||||
--zebra-bg: #202328;
|
||||
--hover-bg: #2a2e38;
|
||||
--active-bg: #1e2640;
|
||||
|
||||
/* Bootstrap bridge tokens */
|
||||
--bs-body-bg: var(--paper);
|
||||
--bs-body-color: var(--ink);
|
||||
--bs-border-color: var(--rule);
|
||||
--bs-emphasis-color: var(--ink);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Caveats
|
||||
|
||||
1. **Bootstrap intrinsic dark utilities**: Bootstrap 5.3 has its own `[data-bs-theme="dark"]` rules for components (alerts, badges, modals, etc.). Our token block sits in site.css at cascade level 0; Bootstrap's own dark-mode block in bootstrap.min.css (loaded earlier) will also fire. The Bootstrap rules and our token rules are additive and complementary — they should not conflict, but any Bootstrap component that ignores our tokens and uses Bootstrap's built-in dark-mode palette may have a slightly different feel. Inspect on first run.
|
||||
|
||||
2. **`body` gradient in theme.css**: The `body` rule in theme.css is:
|
||||
```css
|
||||
body {
|
||||
background: radial-gradient(1200px 480px at 88% -8%, var(--card) 0%, rgba(255,255,255,0) 70%),
|
||||
var(--paper);
|
||||
}
|
||||
```
|
||||
On dark mode, `rgba(255,255,255,0)` in the gradient is transparent white and will remain unchanged. The gradient will still render; `var(--card)` will resolve to the dark card value and the effect will be a subtle lighter sheen in the top-right corner — acceptable, but the gradient can be suppressed in site.css for a flatter dark background if preferred.
|
||||
|
||||
3. **Status tints on dark**: The status background tokens (`--ok-bg`, `--warn-bg`, `--bad-bg`) are deliberately dark-surface equivalents (very low-saturation tints). Verify contrast ratios of status chip text against these backgrounds before shipping.
|
||||
|
||||
4. **`data-bs-theme` placement**: The attribute must be set on `<html>` (or `<body>`) so the CSS selector matches all descendants. The toggle implementation (next task) should write `document.documentElement.setAttribute('data-bs-theme', 'dark')`.
|
||||
Reference in New Issue
Block a user