Files
scadaproj/ZB.MOM.WW.Theme

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

  1. Reference the NuGet package in your app; keep your own Bootstrap 5 <link> in App.razor.

  2. In App.razor <head>, after your Bootstrap link, add <ThemeHead />:

    <link rel="stylesheet" href="..." />  @* Bootstrap — yours, not vendored by the kit *@
    <ThemeHead />
    
  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:

    @* 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