166 lines
8.3 KiB
Markdown
166 lines
8.3 KiB
Markdown
# 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 (~4–95), 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">▮</span> ScadaBridge</div>` (lines ~9–9).
|
||
- 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).
|