docs(ui-theme): current-state ×3 + GAPS adoption backlog

This commit is contained in:
Joseph Doherty
2026-06-01 05:15:38 -04:00
parent 95975d0754
commit 029ac0719b
4 changed files with 579 additions and 0 deletions
@@ -0,0 +1,165 @@
# UI Theme — current state: ScadaBridge
Repo: `~/Desktop/ScadaBridge`. Stack: .NET 10, Blazor SSR (Akka.NET cluster + central UI).
UI surfaces: `src/ZB.MOM.WW.ScadaBridge.CentralUI/` (RCL) and
`src/ZB.MOM.WW.ScadaBridge.Host/` (the Blazor host that references it).
All paths below are relative to the repo root. Verified against source on 2026-06-01.
**Summary:** ScadaBridge uses a sidebar nav layout and the Technical-Light tokens, with the
correct font-path prefix. The sidebar uses `.sidebar` / `.nav-link` classes (same idiom as
MxGateway), not `.side-rail` / `.rail-link`. Adoption is **medium effort** — sidebar-class
migration + `MainLayout` replacement, no layout redesign. ScadaBridge has several
scoped `.razor.css` files that stay per-project.
---
## 1. CSS / design tokens
**`theme.css`** — 379-line hand copy of the Technical-Light design system.
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/css/theme.css`
- Font path: `url('../fonts/ibm-plex-sans-400.woff2')` (lines 24, 29, 34)
- **Correct path** — resolves from `wwwroot/css/` to `wwwroot/fonts/` without 404. This is
the canonical `url('../fonts/…')` that the RCL also uses.
- Wired in the Host's `App.razor` line 9:
`<link href="_content/ZB.MOM.WW.ScadaBridge.CentralUI/css/theme.css" rel="stylesheet" />`.
**`site.css`** — 128 lines of per-app page layout (sidebar shell, nav overrides).
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/css/site.css`
- Wired in the Host's `App.razor` line 11:
`<link href="_content/ZB.MOM.WW.ScadaBridge.CentralUI/css/site.css" rel="stylesheet" />`.
- Contains: `.sidebar` layout block (~495), Bootstrap-icons integration for nav items.
- After adoption: the `.sidebar` layout section is superseded by RCL `layout.css`. The
remaining rules (Bootstrap-icons, misc overrides) stay in `site.css`.
Note: ScadaBridge uses the `_content/ZB.MOM.WW.ScadaBridge.CentralUI/…` static-web-asset
path for its own CentralUI RCL assets — the same mechanism `ZB.MOM.WW.Theme` will use.
---
## 2. IBM Plex fonts
Three `.woff2` files vendored into:
`src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/fonts/`
- `ibm-plex-sans-400.woff2`
- `ibm-plex-sans-600.woff2`
- `ibm-plex-mono-500.woff2`
After adoption: delete all three from `CentralUI/wwwroot/fonts/`; the RCL serves them
from `_content/ZB.MOM.WW.Theme/fonts/`.
---
## 3. Layout shell
**`MainLayout.razor`** — 29-line static layout. `@inherits LayoutComponentBase`. No
`@rendermode` directive (static SSR).
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Layout/MainLayout.razor`
- Root element: `<div class="d-flex flex-column flex-lg-row" style="min-height: 100vh;">`.
No `.app-shell` class.
- Renders `<NavMenu />` inside a Bootstrap collapse div, `<main class="flex-grow-1 p-3">`,
plus `<DialogHost />` and `<SessionExpiry />` at the bottom.
**`NavMenu.razor`** — 200+ line interactive sidebar component. `@implements IDisposable`.
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Layout/NavMenu.razor`
- Brand: `<div class="brand"><span class="mark">&#9646;</span> ScadaBridge</div>` (lines ~99).
- Nav structure: `<nav class="sidebar d-flex flex-column">` with `<ul class="nav flex-column">`
and `<NavSection>` groups ("Admin", "Data", "Audit", etc.) with `<NavLink class="nav-link">`
children. Uses `AuthorizeView` + `AuthorizeView Policy="…"` to gate admin sections.
- Nav state: JS-based expand-state persistence (same pattern as OtOpcUa and MxGateway).
**`NavSection.razor`** (same name as OtOpcUa/MxGateway, independent per-project copy).
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Layout/NavSection.razor`
---
## 4. Login page
**`Login.razor`** — 36-line static login page. `@layout LoginLayout`, `@attribute [AllowAnonymous]`.
- Path: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Login.razor`
- Form: `<form method="post" action="/auth/login" data-enhance="false">` (line 16).
- Uses Bootstrap `.card` / `.card-body` markup — **not** the Technical-Light `.panel` /
`.login-wrap` idiom used in OtOpcUa. Does not use a `<LoginCard>` yet.
- Error notice: Bootstrap `.alert alert-danger` (line 12) rather than `.panel.notice`.
---
## 5. Scoped `.razor.css` files (stays per-project)
ScadaBridge ships several component-scoped CSS files. These are **not shared** and stay
in the CentralUI RCL after adoption:
| File | Path |
|---|---|
| `MultiSelectDropdown.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/` |
| `TreeView.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/` |
| `AuditDrilldownDrawer.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/` |
| `AuditEventDetail.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/` |
| `ExecutionDetailModal.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/` |
| `ExecutionTree.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/` |
| `AuditResultsGrid.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Audit/` |
| `NodeBrowserDialog.razor.css` | `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Dialogs/` |
These scoped styles are component-specific overrides and are unaffected by theme adoption.
---
## 6. Divergences from spec
| Item | Current state | Spec |
|---|---|---|
| `theme.css` | Hand copy, 379 lines | Single canonical copy in RCL |
| Font-path `url()` | `url('../fonts/…')`**correct** | `url('../fonts/…')` — same |
| IBM Plex fonts | Vendored 3× in `wwwroot/fonts/` | Single copy in RCL `wwwroot/fonts/` |
| Shell class | `d-flex …` (no `.app-shell`) | `ThemeShell` + `.app-shell` |
| Nav class idiom | `.sidebar` + `.nav-link` + `<ul>/<li>` | `.side-rail` + `.rail-link` + `<a>` |
| Nav items | `<NavLink class="nav-link">` inside `<li>` | `<NavRailItem>` |
| Nav sections | `NavSection` (`EventCallback OnToggle` + JS) | `NavRailSection` (`<details>`, CSS-only) |
| Status chip | None (uses raw `.chip-*` classes inline) | `StatusPill` (`StatusState` enum) |
| Login card | Bootstrap `.card` markup (not Technical-Light `.panel`) | `<LoginCard>` |
| Scoped `.razor.css` | 8 component-scoped files | Stays per-project (no change) |
---
## 7. Adoption plan
**Effort: Medium. Risk: Medium.** The font path is already correct so no 404 fix needed.
The sidebar idiom migration (`.sidebar``.side-rail`, `.nav-link``.rail-link`) and the
`MainLayout` replacement are the main work. The scoped `.razor.css` files are unaffected.
**Steps:**
1. **Delete copies.** Remove `src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/css/theme.css`
and `wwwroot/fonts/ibm-plex-*.woff2` from `CentralUI`.
2. **Reference RCL.** Add `<PackageReference Include="ZB.MOM.WW.Theme" />` to
`ZB.MOM.WW.ScadaBridge.CentralUI.csproj`. Add `@using ZB.MOM.WW.Theme` to
`CentralUI`'s `_Imports.razor`.
3. **Wire `ThemeHead`.** In `Host`'s `App.razor`, replace line 9
(`<link href="_content/…/css/theme.css">`) with `<ThemeHead />` (which now resolves
via `ZB.MOM.WW.Theme`). Keep the `site.css` link on line 11.
4. **Replace `MainLayout`.** Replace the 29-line `MainLayout.razor` with a thin wrapper
around `<ThemeShell Product="ScadaBridge">`. Carry `<NavMenu />` into the `Nav` slot
(or replace it — see step 5). Keep `<DialogHost />` and `<SessionExpiry />` below the
`ThemeShell` or inside `ChildContent` as needed.
5. **Port nav.** Migrate `NavMenu.razor` from `nav.sidebar` + `<ul>/<li>` + `.nav-link`
to `<NavRailSection>` + `<NavRailItem>`. The `AuthorizeView` policy gating on admin
sections stays — wrap `<NavRailSection>` inside the appropriate `<AuthorizeView>` just
as today.
6. **Clean `site.css`.** Remove the `.sidebar` layout block. Keep Bootstrap-icons
integration and any domain-specific overrides.
7. **Replace login card.** In `Login.razor`, replace the Bootstrap `.card`/`.card-body`
markup with `<LoginCard Product="ScadaBridge" Action="/auth/login" ReturnUrl="@ReturnUrl"
Error="@ErrorMessage"><AntiforgeryToken /></LoginCard>`. Align error display with the
Technical-Light `.panel.notice` style from `LoginCard`.
8. **Keep:** all scoped `.razor.css` files (8 files listed above); `site.css` domain rules;
auth and session endpoints; all page components.
**Risk note:** ScadaBridge's `AuthorizeView` policy-gated nav sections require careful
testing — verify that `<NavRailSection>` inside `<AuthorizeView>` renders correctly and
that the section is fully hidden when the policy fails (not just collapsed).