# UI Theme — current state: MxAccessGateway

Repo: `~/Desktop/MxAccessGateway` (Gitea `mxaccessgw`). Stack: .NET 10, Blazor SSR
(gateway x64) — UI in `src/ZB.MOM.WW.MxGateway.Server/`.
All paths below are relative to the repo root. Verified against source on 2026-06-01.

**Summary:** MxAccessGateway uses a sidebar nav layout and the Technical-Light tokens, but
the sidebar uses Bootstrap `.sidebar` / `.nav-link` classes rather than the canonical
`.side-rail` / `.rail-link` classes, and the overall structure diverges from the spec
target. Adoption has the **highest effort and risk** of the three apps — the shell
requires migration from its current sidebar idiom to the canonical `ThemeShell` pattern.
There is no dedicated login page (authentication gate is integrated into the Dashboard).

---

## 1. CSS / design tokens

**`theme.css`** — 379-line hand copy of the Technical-Light design system. Identical in
content to OtOpcUa's and ScadaBridge's copies except for the font-path prefix.
- Path: `src/ZB.MOM.WW.MxGateway.Server/wwwroot/css/theme.css`
- Font path: `url('/fonts/ibm-plex-sans-400.woff2')` (lines 24, 29, 34)
- Absolute path (`/fonts/…`) is technically correct (resolves from root of the app), but
  differs from the canonical `url('../fonts/…')` in the RCL — a deployment path difference,
  not a loading bug.
- Wired in `App.razor` line 6: `<link rel="stylesheet" href="/css/theme.css" />`.

**`site.css`** — 592 lines of per-app page layout and Dashboard component styling.
- Path: `src/ZB.MOM.WW.MxGateway.Server/wwwroot/css/site.css`
- Wired in `App.razor` line 7: `<link rel="stylesheet" href="/css/site.css" />`.
- Contains: `.sidebar` layout (lines ~24–95), `.dashboard-body`, `.agg-card`, table
  styles, metric cards, event/alarm grids, and other domain-specific rules.
- After adoption: the `.sidebar` layout section is superseded by RCL `layout.css`.
  The domain-specific table/card/grid rules stay in `site.css`.

Note: MxGateway's `App.razor` loads assets from `/css/…` and `/fonts/…` (root-relative
paths to `wwwroot/`), not via `_content/…` static-web-asset paths — contrast with
OtOpcUa and ScadaBridge which use the RCL `_content/` mechanism.

---

## 2. IBM Plex fonts

Three `.woff2` files vendored into:
`src/ZB.MOM.WW.MxGateway.Server/wwwroot/fonts/`
- `ibm-plex-sans-400.woff2`
- `ibm-plex-sans-600.woff2`
- `ibm-plex-mono-500.woff2`

After adoption: delete all three; the RCL serves them from
`_content/ZB.MOM.WW.Theme/fonts/`.

---

## 3. Layout shell

**`MainLayout.razor`** — 210-line combined layout + nav component. `@implements
IDisposable`; `@inject NavigationManager`, `@inject IJSRuntime`. Interactive
(`@rendermode InteractiveServer` inherited from `Routes`).
- Path: `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/Layout/MainLayout.razor`
- Root element: `<div class="d-flex flex-column flex-lg-row" style="min-height: 100vh;">`.
  Note: **no `.app-shell` class** (unlike OtOpcUa and the spec target).
- Brand: `<a class="brand" href="/"><span class="mark">&#9646;</span> MXAccess Gateway</a>`
  (line 24).
- Nav structure: `<nav class="sidebar d-flex flex-column">` with `<ul class="nav flex-column">`
  and `<NavSection>` groups ("Runtime", "Galaxy", "Admin", "Configuration") with
  `<NavLink class="nav-link">` children (not `.rail-link`).
- No dedicated `RailFooter` / session block (auth state shown elsewhere or via API keys).
- Nav state persisted via JS (`nav-state.js`), same pattern as OtOpcUa.

**`NavSection.razor`** — 40-line component using `EventCallback OnToggle` + JS collapse.
- Path: `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/Layout/NavSection.razor`
- Structure: `<li class="nav-item"><button class="nav-section-toggle" @onclick="OnToggle">`.
  Uses Bootstrap-style `<ul>/<li>` nav items, not `.rail-link` anchor style.

---

## 4. Login / auth surface

MxAccessGateway has **no dedicated Blazor login page**. There is no `Login.razor`. The
dashboard is protected by ASP.NET Core cookie authentication; login is handled via an
ASP.NET Minimal API auth endpoint (outside the Blazor component tree). The `MainLayout`
includes a "Sign In" link (`<a href="/login" class="btn …">Sign In</a>` line 87) that
redirects to the server endpoint. The `<LoginCard>` component is not applicable until
a Blazor login page is added.

---

## 5. StatusBadge component

**`StatusBadge.razor`** — string-match–based chip component.
- Path: `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/Shared/StatusBadge.razor`
- Parameters: `string? Text`. Maps known strings ("Ready", "Healthy", "Active", "Faulted",
  etc.) to `chip-ok` / `chip-warn` / `chip-bad` / `chip-idle` CSS classes.
- After adoption: the string-matching logic is app-specific (based on gateway session
  state strings). Migration to `StatusPill` requires the caller to map gateway state
  strings to `StatusState` values, then pass `State` instead of `Text`.

---

## 6. Divergences from spec

| Item | Current state | Spec |
|---|---|---|
| `theme.css` | Hand copy, 379 lines | Single canonical copy in RCL |
| Font-path `url()` | `url('/fonts/…')` (absolute, not a bug but non-canonical) | `url('../fonts/…')` |
| IBM Plex fonts | Vendored 3× in `wwwroot/fonts/` | Single copy in RCL `wwwroot/fonts/` |
| Asset wiring | Root-relative `/css/…`, `/fonts/…` | `_content/ZB.MOM.WW.Theme/…` |
| 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` (button `OnToggle` + JS) | `NavRailSection` (`<details>`, CSS-only) |
| Status chip | `StatusBadge` (string text → CSS class) | `StatusPill` (`StatusState` enum) |
| Login page | None — server endpoint redirect only | `<LoginCard>` (if a Blazor login page is added) |

---

## 7. Adoption plan

**Effort: High. Risk: High.** The sidebar idiom (`nav.sidebar` + `.nav-link` + `<ul><li>`)
differs from the canonical rail idiom (`.side-rail` + `.rail-link` + `<a>`), requiring a
CSS and markup migration. No layout redesign (it already uses a side-panel pattern), but
class names, element structure, and the `site.css` sidebar block all change.

**Steps:**

1. **Delete copies.** Remove `wwwroot/css/theme.css` and `wwwroot/fonts/ibm-plex-*.woff2`
   from `src/ZB.MOM.WW.MxGateway.Server/`.

2. **Reference RCL.** Add `<PackageReference Include="ZB.MOM.WW.Theme" />` to
   `ZB.MOM.WW.MxGateway.Server.csproj`. Add `@using ZB.MOM.WW.Theme` to `_Imports.razor`.

3. **Wire `ThemeHead`.** In `App.razor` replace `/css/theme.css` link with `<ThemeHead />`.
   Keep `/css/site.css` for domain-specific rules. Also change static asset paths from
   root-relative to `_content/ZB.MOM.WW.Theme/…` for fonts if any remain in `site.css`.

4. **Replace `MainLayout`.** Replace the 210-line `MainLayout.razor` with a thin wrapper
   around `<ThemeShell Product="MXAccess Gateway">`. Carry the nav sections and the sign-in
   link into the `Nav` and `RailFooter` slots respectively.

5. **Port nav items.** Migrate from `<NavLink class="nav-link">` inside `<li class="nav-item">`
   to `<NavRailItem Href="…" Text="…">`. The four section groups ("Runtime", "Galaxy",
   "Admin", "Configuration") map to `<NavRailSection Title="…">` children.

6. **Clean `site.css`.** Remove the `.sidebar` layout block (lines ~24–95) — superseded by
   `layout.css`. Keep all dashboard/domain-specific rules (`.agg-card`, tables, metric
   cards, etc.).

7. **Replace `StatusBadge`.** Add a helper that maps gateway session-state strings to
   `StatusState` values; replace `<StatusBadge Text="…">` call sites with
   `<StatusPill State="…">`. Delete `StatusBadge.razor`.

8. **Login card (optional).** No Blazor login page exists today. If one is added,
   `<LoginCard>` is the canonical implementation.

9. **Keep:** domain-specific `site.css` rules, scoped `.razor.css` files (none currently),
   API-key authentication, all page components.

**Flagged risk:** This is the largest UX-visible change across the three apps. The sidebar
class migration (`.sidebar` → `.side-rail`, `.nav-link` → `.rail-link`) will visually
change the nav styling. Verify visually in the dashboard before merging. The nav expand
state persistence (JS-based) must be verified or replaced with CSS-only `<details>`.
