# UI Theme β gaps & adoption backlog
Divergence of each project from [`spec/SPEC.md`](spec/SPEC.md), and the ordered backlog to
reach adoption of the `ZB.MOM.WW.Theme` shared RCL. Status legend: β gap Β· π‘ partial Β· β matches.
> **β ADOPTED 2026-06-03 (local-only).** Backlog #2β#4 implemented across all three apps on each repo's
> **`feat/adopt-zb-theme`** branch β full canonical cutover (SPEC Β§7): ``/``,
> thin `MainLayout` β `` + `NavRailItem`/`NavRailSection`, per-app `theme.css`/IBM-Plex fonts/
> `nav-state.js` deleted, `` sign-in, and `StatusPill` (OtOpcUa's dead `StatusBadge` deleted;
> MxGateway's `StatusBadge` redirected to a thin `StatusPill` adapter; inline domain `.chip-*` kept as page
> content per Β§6). **Library first enhanced to `0.2.0`** β nav-expand persistence promoted INTO the kit
> (`NavRailSection.Key` β `data-nav-key` + a localStorage `nav-state.js` enhancer emitted by a new
> ``), so all three apps get uniform persistence from one source (OtOpcUa's bespoke
> cookie/JS-interop nav island retired). 0.2.0 published to the Gitea feed; 44 bUnit tests. **MxGateway
> additionally gained a net-new Blazor `` `/login` page** reusing its existing hardened
> `POST /login` endpoint (antiforgery + `SanitizeReturnUrl` + `SignInAsync` preserved). Every task spec+code
> reviewed (high-risk via serial specβcode; the MxGateway login via an Opus security review), then
> **fast-forward-merged into each repo's local default and PUSHED to origin (gitea) 2026-06-03** (in sync;
> `feat/*` kept locally): OtOpcUa `master`@`11de14d`, ScadaBridge `main`@`58352a6`, MxGateway `main`@`73e54e2`.
> Plan: `docs/plans/2026-06-03-ui-theme-adoption*.md`. The β/π‘ cells below describe the PRE-adoption
> divergence (kept for history).
>
> **Post-adoption CSS prune (2026-06-03, branch `chore/theme-css-prune` per app).** An audit found each app's
> kept `site.css` still carried the old shell CSS the kit now owns β broader than first logged. Pruned:
> **OtOpcUa** shed a near-verbatim copy of the kit's `layout.css` (`.app-shell`/`.side-rail`/`.rail-link`/
> `.rail-foot`/`.login-*`) plus dead `#sidebar-collapse` (kit emits `#theme-rail`) and `.rail-eyebrow-chevron`
> (β167 lines), keeping only app-only `.rail-eyebrow` + `.chip-alert`/`.chip-caution`; **ScadaBridge** shed the
> dead `.sidebar`/`.nav-link`/`.nav-section-toggle` block (β95), keeping `#reconnect-modal`/`.script-editor-modal`;
> **MxGateway** shed the dead `.sidebar` block + orphaned `.dashboard-login`/`.login-card` (β106), keeping
> `.app-bar` (still used by `/denied`) + the `.chip` override. Each verified unreferenced before removal; all
> three build clean (0 warn/0 err). OtOpcUa's copy was the notable one β it *overrode* the kit, not just dead code.
> **Still deferred:** a kit-side `layout.css` `calc(100vh - 3.3rem)` review; and ScadaBridge's `Host` consumes the
> kit only **transitively via `CentralUI`** (no direct `PackageReference`) β builds green, but an implicit dependency.
>
> _Feed note: the same audit re-confirmed `ZB.MOM.WW.Theme 0.2.0` **is** genuinely on the Gitea feed (registration
> `count:1`, package base `versions:["0.2.0"]`, search `totalHits:1`) β the publish was real, not optimism._
---
## Divergence vs spec
### Β§1 Design tokens β `theme.css`
| Item | OtOpcUa | MxAccessGateway | ScadaBridge |
|---|---|---|---|
| Tokens identical to canonical | β identical | β identical | β identical |
| File maintained in one place (RCL) | β own copy | β own copy | β own copy |
| Font path `url('../fonts/β¦')` | β `url('fonts/β¦')` β **latent 404** | π‘ `url('/fonts/β¦')` β absolute, not portable | β `url('../fonts/β¦')` β correct |
| IBM Plex fonts in one place | β own `wwwroot/fonts/` | β own `wwwroot/fonts/` | β own `wwwroot/fonts/` |
β **Gap T1:** All three apps maintain a copy of `theme.css` β the single-source guarantee
is broken today. Any token change must be applied in four places (three apps + the RCL)
once the RCL exists.
β **Gap T2:** OtOpcUa `url('fonts/β¦')` is a latent 404 masked by system-font fallback.
Adoption fixes it automatically.
β **Gap T3:** Each app vendors fonts β 3Γ duplication. The RCL eliminates it.
### Β§2 Typography
All three apps reference IBM Plex via the token stacks. No typography divergence β the
token values are identical. Gap is delivery (T3 above).
### Β§3 Canonical side-rail layout
| Item | OtOpcUa | MxAccessGateway | ScadaBridge |
|---|---|---|---|
| `.app-shell` root element | β `div.app-shell` | β `div.d-flex β¦` (no `.app-shell`) | β `div.d-flex β¦` (no `.app-shell`) |
| Rail CSS class | β `.side-rail` | β `.sidebar` | β `.sidebar` |
| Nav item CSS class | β `.rail-link` | β `.nav-link` | β `.nav-link` |
| Nav item element | β `` (NavLink) | β `