# ZB.MOM.WW.Theme Shared Technical-Light UI kit for the ZB.MOM.WW SCADA family: design tokens + IBM Plex fonts (static web assets) and a canonical side-rail shell + widgets. The kit ships one .NET 10 Razor Class Library (RCL) with CSS custom-property tokens, the three IBM Plex woff2 fonts, and side-rail layout CSS — all served from `_content/ZB.MOM.WW.Theme/…` — plus a set of Blazor SSR components that carry no inline colours and reuse the token classes. Bootstrap 5 is **not** vendored; each app keeps its own Bootstrap link. The kit ships **no JavaScript**: every interactive affordance — the narrow-viewport side-rail hamburger and the collapsible nav sections — is a native CSS-only `
`/`` disclosure, so the chrome works unchanged in static Blazor SSR with only Bootstrap **CSS** present (no Bootstrap collapse JS bundle required). ## Adopt 1. Reference the NuGet package in your app; keep your own Bootstrap 5 `` in `App.razor`. 2. In `App.razor` ``, **after** your Bootstrap link, add ``: ```razor @* Bootstrap — yours, not vendored by the kit *@ ``` 3. Make your `MainLayout` a thin delegate to `ThemeShell` — `@layout` cannot pass parameters, so `ThemeShell` is a regular component and `MainLayout` is a 3-line wrapper: ```razor @* Layouts/MainLayout.razor *@ @inherits LayoutComponentBase @* session info / sign-out link *@ @Body ``` Add `@using ZB.MOM.WW.Theme` to your `_Imports.razor` so all components are available without per-file usings. ### Login page Use `` for the sign-in form. The card posts to a server endpoint (`/auth/login` by default) so `SignInAsync` can run before the response starts. You **must** inject `` inside the card and **validate `ReturnUrl` server-side** before redirecting (open-redirect risk): ```razor ``` ## Components | Component | Parameters (key) | Notes | |---|---|---| | `ThemeHead` | — | Emits `` tags for `theme.css` and `layout.css`. Place in `` after Bootstrap. | | `ThemeShell` | `Product`*, `Accent`, `Logo`, `Nav`, `RailFooter`, `ChildContent` | Side-rail chassis. Not a layout — delegated to from `MainLayout`. `Accent` overrides `--accent` token. | | `BrandBar` | `Product`*, `Logo` | Brand glyph + product name; rendered inside `ThemeShell`'s rail header. | | `NavRailItem` | `Href`*, `Text`*, `Icon`, `Match` | Wraps ``. | | `NavRailSection` | `Title`*, `Expanded` (default `true`), `ChildContent` | CSS-only collapsible `
` group; no JS, works in static SSR. | | `StatusPill` (`StatusState`) | `State`* (`Ok`/`Warn`/`Bad`/`Idle`/`Info`), `ChildContent` | Inline chip. `StatusState` enum is in `ZB.MOM.WW.Theme`. | | `LoginCard` | `Product`*, `Action` (default `/auth/login`), `ReturnUrl`, `Error`, `ChildContent` | Static form-POST sign-in card. Inject `` via `ChildContent`; validate `ReturnUrl` server-side. | | `TechButton` (`ButtonVariant`) | `Variant` (`Primary`/`Secondary`/`Danger`/`Ghost`), `Type`, `Busy`, `ChildContent`, splatted attrs | `Busy` disables the button and shows a spinner. `ButtonVariant` enum is in `ZB.MOM.WW.Theme`. | | `TechCard` | `Title`, `Header`, `ChildContent`, `Footer`, `Class` | Panel with optional head/body/footer slots. | | `TechField` | `Label`*, `Hint`, `Error`, `ChildContent` | Form field wrapper with label, hint text, and inline error. | \* `EditorRequired` parameter. Flat namespace: all components and enums live in `ZB.MOM.WW.Theme`. One `@using` covers everything. ## Static assets Served at `_content/ZB.MOM.WW.Theme/…` by the ASP.NET static-web-asset pipeline: | Path | Contents | |---|---| | `css/theme.css` | Design tokens (`--accent`, `--ok`, `--warn`, …), typography, utility helpers | | `css/layout.css` | Side-rail shell layout, collapsible nav, `StatusPill` variants, card/field helpers | | `fonts/ibm-plex-sans-400.woff2` | IBM Plex Sans Regular | | `fonts/ibm-plex-sans-600.woff2` | IBM Plex Sans SemiBold | | `fonts/ibm-plex-mono-500.woff2` | IBM Plex Mono Medium | `theme.css` declares `@font-face` with `url('../fonts/…')` — correct relative path from `css/` to `fonts/`. (OtOpcUa's original `url('fonts/…')` was a latent 404; the kit fixes it.) All colours are CSS custom-property tokens declared once in `theme.css`'s `:root` block — no hardcoded hex appears outside it. Derived border/hover/wash shades (e.g. `--ok-border`, `--warn-ink`, `--info-bg`, `--hover-bg`, `--active-bg`, `--zebra-bg`) are named tokens too, so a token-block edit re-themes the whole kit. ## Build ```bash # from ZB.MOM.WW.Theme/ dotnet build -c Release # TreatWarningsAsErrors — expect 0 warnings dotnet test # 38 bUnit tests ./build/pack.sh # → ./artifacts/ZB.MOM.WW.Theme.0.1.0.nupkg ```