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
<details>/<summary> disclosure, so the chrome works unchanged in static Blazor SSR
with only Bootstrap CSS present (no Bootstrap collapse JS bundle required).
Adopt
-
Reference the NuGet package in your app; keep your own Bootstrap 5
<link>inApp.razor. -
In
App.razor<head>, after your Bootstrap link, add<ThemeHead />:<link rel="stylesheet" href="..." /> @* Bootstrap — yours, not vendored by the kit *@ <ThemeHead /> -
Make your
MainLayouta thin delegate toThemeShell—@layoutcannot pass parameters, soThemeShellis a regular component andMainLayoutis a 3-line wrapper:@* Layouts/MainLayout.razor *@ @inherits LayoutComponentBase <ThemeShell Product="OtOpcUa" Accent="#2f5fd0"> <Nav> <NavRailSection Title="Navigation"> <NavRailItem Href="/" Text="Overview" Match="NavLinkMatch.All" /> <NavRailItem Href="/clusters" Text="Clusters" /> </NavRailSection> </Nav> <RailFooter>@* session info / sign-out link *@</RailFooter> <ChildContent>@Body</ChildContent> </ThemeShell>
Add @using ZB.MOM.WW.Theme to your _Imports.razor so all components are available
without per-file usings.
Login page
Use <LoginCard> 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 <AntiforgeryToken/> inside the card and validate ReturnUrl
server-side before redirecting (open-redirect risk):
<LoginCard Product="OtOpcUa" Action="/auth/login" ReturnUrl="@safeReturnUrl" Error="@errorMsg">
<AntiforgeryToken />
</LoginCard>
Components
| Component | Parameters (key) | Notes |
|---|---|---|
ThemeHead |
— | Emits <link> tags for theme.css and layout.css. Place in <head> 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 <NavLink class="rail-link">. |
NavRailSection |
Title*, Expanded (default true), ChildContent |
CSS-only collapsible <details> 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 <AntiforgeryToken/> 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
# 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