Files
scadaproj/components/ui-theme/current-state/otopcua/CURRENT-STATE.md
T
2026-06-01 05:15:38 -04:00

7.9 KiB
Raw Blame History

UI Theme — current state: OtOpcUa

Repo: ~/Desktop/OtOpcUa (Gitea lmxopcua). Stack: .NET 10, Blazor SSR. UI surface: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ (Razor Class Library). All paths below are relative to the repo root. Verified against source on 2026-06-01.

Summary: OtOpcUa already uses a side-rail layout and the full Technical-Light token set. Adoption is lowest effort of the three apps — the shell shape already matches the canonical target. The one bug fixed by adoption: a latent font-path 404 that silently falls back to system fonts today.


1. CSS / design tokens

theme.css — 379-line hand copy of the Technical-Light design system.

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/theme.css
  • Font path: url('fonts/ibm-plex-sans-400.woff2') (lines 24, 29, 34)
  • Bug: the path is relative to the CSS file location (wwwroot/css/), so it resolves as wwwroot/css/fonts/… — a 404. The browser silently falls back to system fonts. The canonical RCL path url('../fonts/…') fixes this permanently.
  • Wired in App.razor line 17: <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/theme.css"/>.

site.css — 174 lines of per-app page layout (side-rail shell, login card layout, page body padding, miscellaneous overrides).

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css
  • Wired in App.razor line 18: <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>.
  • After adoption: the .side-rail, .rail-*, .login-wrap, .login-title rules are superseded by the RCL's layout.css. The page-layout residuals (body padding, page- specific overrides) stay in site.css.

2. IBM Plex fonts

Three .woff2 files vendored into: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/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 — 28-line static layout (no @rendermode). Renders .app-shell flex row, hamburger toggle, <NavSidebar/> inside a Bootstrap collapse div, and <main class="page">.

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/MainLayout.razor
  • Structure: .app-shell d-flex flex-column flex-lg-row (line 8), hamburger (lines 1118), <div class="collapse d-lg-block" id="sidebar-collapse"> (line 21), <NavSidebar /> (line 22), <main class="page">@Body</main> (lines 2527).

NavSidebar.razor — 160-line interactive (@rendermode InteractiveServer) sidebar. Hosts the collapsible NavSection groups and cookie-persisted expand state.

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/NavSidebar.razor
  • Brand: <div class="brand"><span class="mark">&#9646;</span> OtOpcUa</div> (lines 1414).
  • Nav sections: two NavSection groups ("Navigation", "Scripting", "Live", "Config") with <NavLink class="rail-link"> children.
  • Rail foot (lines 4462): <div class="rail-foot"><AuthorizeView> — session info + sign-out <form method="post" action="/auth/logout">.
  • Nav expand state persisted in otopcua_nav cookie via wwwroot/js/nav-state.js (cookie: otopcua_nav=<comma-separated ids>).

NavSection.razor — 36-line NavSection component (interactive; uses EventCallback OnToggle for expand/collapse, not CSS <details>).

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/NavSection.razor

LoginLayout.razor — plain layout (no sidebar) used by the login page.

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/LoginLayout.razor

4. Login page

Login.razor — 50-line static login page. Uses @layout LoginLayout, @attribute [AllowAnonymous].

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Login.razor
  • Form: <form method="post" action="/auth/login" data-enhance="false"> (line 21).
  • Hidden returnUrl input (line 2225), username/password inputs, error notice panel.
  • The form structure exactly matches what <LoginCard> emits; migration is direct.

5. StatusBadge component

StatusBadge.razor — thin wrapper over .chip classes.

  • Path: src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/StatusBadge.razor
  • Parameters: string Text, string CssClass (default chip-idle).
  • After adoption: replaced by <StatusPill State="…"> — caller maps state to StatusState enum rather than passing CSS class strings directly.

6. Divergences from spec

Item Current state Spec
theme.css Hand copy, 379 lines Single canonical copy in RCL
Font-path url() url('fonts/…')latent 404 url('../fonts/…') — correct
IBM Plex fonts Vendored 3× in wwwroot/fonts/ Single copy in RCL wwwroot/fonts/
Shell layout .app-shell + NavSidebar component (matches target shape) ThemeShell + thin MainLayout
Nav items <NavLink class="rail-link"> inside interactive NavSidebar NavRailItem inside NavRailSection
Nav expand state Cookie-persisted via otopcua_nav + JS CSS-only <details> in NavRailSection
Status chip StatusBadge (string CSS class param) StatusPill (StatusState enum param)
Login card Inline markup in Login.razor <LoginCard>

7. Adoption plan

Effort: Low. The shell shape already matches the target. No layout migration needed.

Steps:

  1. Delete copies. Remove wwwroot/css/theme.css and wwwroot/fonts/ibm-plex-*.woff2 from ZB.MOM.WW.OtOpcUa.AdminUI. This also fixes the latent font-path 404.

  2. Reference RCL. Add <PackageReference Include="ZB.MOM.WW.Theme" /> to ZB.MOM.WW.OtOpcUa.AdminUI.csproj. Add @using ZB.MOM.WW.Theme to _Imports.razor.

  3. Wire ThemeHead. In App.razor replace lines 1718:

    - <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/theme.css"/>
    - <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>
    + <ThemeHead />
    + <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>
    

    (Keep site.css for the page-layout residuals.)

  4. Replace MainLayout. Delete the current 28-line MainLayout.razor. Create a new thin MainLayout.razor that delegates to <ThemeShell Product="OtOpcUa Admin"> with Nav and RailFooter slots (carry the session/sign-out block from NavSidebar's .rail-foot into RailFooter).

  5. Port nav. Rebuild the Nav slot using <NavRailSection> + <NavRailItem>. The four section groups ("Navigation", "Scripting", "Live", "Config") map directly to NavRailSection Title="…" with NavRailItem children.

    Cookie nav state: OtOpcUa's otopcua_nav cookie persistence requires JS and an InteractiveServer component. If this feature is retained, keep a bespoke interactive NavSection (the current NavSection.razor or a refactored version) alongside — it is compatible with ThemeShell's Nav slot. If cookie persistence is acceptable to drop, NavRailSection (CSS-only <details>) is a drop-in replacement.

  6. Replace StatusBadge. Find all usages of <StatusBadge CssClass="chip-*"> and replace with <StatusPill State="StatusState.*">. Delete StatusBadge.razor.

  7. Replace login card. In Login.razor, replace the inline <div class="login-wrap"> … </div> block with <LoginCard Product="OtOpcUa Admin" Action="/auth/login" ReturnUrl="@ReturnUrl" Error="@Error"><AntiforgeryToken /></LoginCard>. The code-behind (Error / ReturnUrl supply-from-query properties) stays unchanged.

  8. Keep: site.css page-layout residuals; scoped .razor.css files (none currently in AdminUI); LoginLayout.razor; auth endpoints; all page components.

Risk: Low — layout shape already matches, no top-bar migration. Cookie nav state is the only optional complexity (decide retain vs drop).