Files
mxaccessgw/docs/DashboardInterfaceDesign.md
T
Joseph Doherty e541339c07 docs(audit): apply per-cluster judgment fixes across living docs
Resolve audit findings: correct WorkerEnvelope proto/route/metric/session
facts; rewrite auth (ZB.MOM.WW.Auth migration), dashboard (ZB.MOM.WW.Theme),
and StyleGuide (foreign-project copy-paste); document alarm subsystem, Ldap
options, and gateway alarm broker; fix client CLI flags and package paths.
2026-06-03 16:01:28 -04:00

13 KiB

Dashboard Interface Design

This guide describes the visual and interaction patterns used by the MXAccess Gateway dashboard so the same interface style can be reused in other operations-focused projects.

Design Goal

The dashboard is an operational interface, not a landing page. It prioritizes fast scanning, low visual noise, and stable layouts while live data changes. The layout chrome, status presentation, and design tokens come from the shared ZB.MOM.WW.Theme kit (the technical-light design system). Bootstrap supplies common widget behavior, and a small local stylesheet (wwwroot/css/site.css) wires the dashboard's own class names and Bootstrap widgets onto the kit's tokens. The local sheet contains no hard-coded colors; every color, font, and surface resolves to a theme token.

Use this style for applications where users repeatedly check system state, compare rows, inspect details, and diagnose faults. Avoid promotional layouts, large hero areas, decorative imagery, or oversized cards that reduce data density.

Visual Language

The interface uses a quiet, work-focused visual system:

  • A light gray page background separates the application shell from white data surfaces.
  • White cards and sections carry the actual operational content.
  • Borders define structure more often than shadows.
  • Accent color is reserved for metric values and important numeric signals.
  • The kit's StatusPill provides state color without custom status art.
  • Tables remain compact and responsive so long identifiers and timestamps stay readable.

The resulting page should look like a control surface: restrained, predictable, and dense enough for repeated use.

Layout Structure

The application chassis is the kit's ThemeShell component (a vertical side rail plus a content area), not a horizontal top navbar. MainLayout.razor is a thin wrapper that delegates the rail chassis — brand block, hamburger toggle, responsive collapse — to <ThemeShell> and supplies only the navigation items and a rail footer:

<ThemeShell Product="MXAccess Gateway" Accent="#2f5fd0">
    <Nav>
        <NavRailItem Href="/" Text="Dashboard" Match="NavLinkMatch.All" />
        <NavRailSection Title="Runtime" Key="runtime">
            <NavRailItem Href="/sessions" Text="Sessions" />
            <NavRailItem Href="/workers" Text="Workers" />
        </NavRailSection>
    </Nav>
    <RailFooter><!-- user name + sign-out --></RailFooter>
    <ChildContent>@Body</ChildContent>
</ThemeShell>

Within the content area, every page follows the same structure:

  1. A page header with the page title, short context text, and optional status pill.
  2. Metric cards when a page has top-level numeric state.
  3. Bordered content sections for tables, details, faults, or empty states.

The login page uses LoginLayout.razor instead — a minimal layout with no rail and no brand block, because the page renders its own centered <LoginCard>.

Color Tokens

Colors come from the ZB.MOM.WW.Theme kit's theme.css. The local site.css defines no :root custom properties of its own; it references kit tokens by name. The dashboard does not define a --mxgw-* token set.

Token Purpose
var(--card) Background of cards, sections, and data tables.
var(--rule), var(--rule-strong) Hairline and stronger borders.
var(--ink), var(--ink-soft), var(--ink-faint) Primary, secondary, and muted text.
var(--accent), var(--accent-deep) Metric values, links, primary buttons, focus rings.
var(--mono) Monospace family for values, identifiers, and code.
var(--ok)/--ok-bg, var(--warn)/--warn-bg, var(--bad)/--bad-bg, var(--idle)/--idle-bg State colors for chips, alerts, and alarm-state labels.

Keep the palette small and let the kit own it. Add new colors only when they encode state or improve readability, and resolve them to a kit token rather than a literal hex value. Use the kit's StatusPill for states such as ready, closing, idle, and faulted.

Typography

Typography stays compact and consistent:

  • Page headings (.dashboard-page-header h1) use 1.15rem, weight 600, and a slight letter spacing.
  • Section headings (.section-heading h2) use a small uppercase eyebrow: .74rem, weight 600, muted ink.
  • Metric labels (.agg-label) use uppercase text at .68rem and weight 600, muted ink.
  • Metric values (.agg-value) use 1.5rem, weight 600, the monospace family, tabular numerics, and primary ink (var(--ink)).
  • Body and table text inherit Bootstrap defaults for readability.

Do not scale text with viewport width. Long values use overflow-wrap: break-word (numbers and date tokens stay whole, wrapping only at spaces); a few free-form fields such as .agg-sub use overflow-wrap: anywhere so session IDs, paths, and fault messages do not break the layout.

Spacing And Shape

The dashboard uses modest spacing:

  • The kit owns the rail and content padding; the local small-screen rule sets .page padding to .85rem.
  • Metric grids use .75rem gaps.
  • Content sections (.dashboard-section) and metric cards (.agg-card) are fully bordered cards: var(--card) fill, a 1px solid var(--rule) hairline, and 0.9rem padding for sections.
  • Cards, sections, and modals use an 8px radius; smaller widgets such as the empty state use 6px.
  • Metric cards have no shadow (box-shadow: none); borders define structure.

This keeps information grouped without turning each section into a decorative panel. Use cards for repeated metric summaries, login forms, and individual items. Use bordered sections for page-level groups.

Navigation

Navigation lives in the ThemeShell side rail. It is built from the kit's NavRailSection and NavRailItem components: a single home item plus eight page items grouped into three labeled sections.

Section Items
(home) Dashboard (route /, NavLinkMatch.All)
Runtime Sessions, Workers, Events, Alarms
Galaxy Repository, Browse
Admin API Keys, Settings

Section expand/collapse state is owned by the kit (a <details> element plus ThemeScripts); the layout does not run JS interop for it. The rail footer shows the signed-in user name and a sign-out form (or a sign-in link when unauthenticated).

Keep navigation labels short and group related pages. Operational users should be able to predict what each page contains without reading explanatory copy.

Page Headers

Each page starts with a dashboard-page-header:

  • The title is the primary anchor.
  • A single secondary line gives timestamp, row count, or configuration context.
  • A status pill appears on the right when the page has an overall state.

On narrow screens, the header stacks vertically. This prevents long context text or status pills from overlapping the title.

<div class="dashboard-page-header">
  <div>
    <h1>Dashboard</h1>
    <div class="text-secondary">Generated 2026-04-27 17:30:00</div>
  </div>
  <!-- <StatusBadge Text="Healthy" /> -> kit <StatusPill State="Ok"> -->
</div>

Metric Cards

Metric cards summarize numeric state at the top of the home and diagnostic pages. The MetricCard component renders an .agg-card with label, value, and optional sub-line:

  • Label (.agg-label): uppercase eyebrow, muted, compact.
  • Value (.agg-value): large monospace number in primary ink, wraps safely.
  • Sub (.agg-sub): optional muted text for version, rate context, or state.

Cards lay out in a .metric-grid. Use auto-fill CSS grid tracks so they fill available width without custom breakpoints:

.metric-grid {
  display: grid;
  gap: .75rem;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
}

.metric-grid.compact {
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
}

Metrics should be formatted before rendering. Counts use thousands separators, durations use stable units, and missing values render as -.

Tables

Tables are the main information surface. Use Bootstrap table table-sm with a local dashboard-table class:

  • table-sm keeps rows dense.
  • align-middle improves status badge alignment.
  • table-responsive wraps every table that can exceed the viewport.
  • Header cells use weight 650 and no wrapping.
  • Body cells allow wrapping so identifiers, paths, and messages stay visible.
  • Detail tables reserve a fixed header width.

Use code formatting for machine identifiers such as session IDs, file paths, and protocol values. Link rows only where navigation is useful; avoid making entire rows clickable when a single identifier link is clearer.

Status Badges

StatusBadge is a thin adapter over the kit's StatusPill. Call sites pass the literal domain state text (<StatusBadge Text="Ready" />); the adapter maps that text to one of the kit's four StatusState values, and StatusPill renders the chip. There are no Bootstrap text-bg-* classes in this layer.

Domain state text StatusState
Ready, Healthy, Active Ok
Creating, StartingWorker, WaitingForPipe, InitializingWorker, Closing, Stale, Degraded Warn
Faulted, Unavailable Bad
Any other text (including Closed, Revoked, Unknown) Idle

Note the mapping changes from earlier revisions: Closed now falls through to Idle (rather than its own neutral badge), and Active, Stale, Degraded, and Unavailable are explicit cases. The kit owns the chip rendering; only this domain text-to-state vocabulary lives in the app.

Keep status text literal. Operators benefit from seeing the same state names that appear in logs and APIs.

Empty And Loading States

Empty states are explicit and quiet. They use a white background, dashed border, small radius, muted text, and one sentence:

<div class="empty-state">No worker processes are attached.</div>

Loading states use the same component shape. Avoid spinners for snapshot pages that update on a timer; a stable text placeholder is less distracting.

Detail Pages

Detail pages use stacked sections instead of nested cards:

  • The page header identifies the selected entity.
  • The first section shows entity metadata in a two-column details table.
  • Additional sections show related runtime state, such as worker metadata.
  • Missing entities render a single section with a concise not-found message.

This structure keeps details comparable across pages and avoids card nesting.

Responsive Behavior

The dashboard uses one small-screen breakpoint:

@media (max-width: 700px) {
  .page {
    padding: .85rem;
  }

  .dashboard-page-header {
    align-items: flex-start;
    flex-direction: column;
  }

  .details-table th {
    width: 9rem;
  }
}

A second breakpoint (max-width: 960px) collapses the Browse two-pane layout (.browse-layout) to a single column.

Do not hide important columns by default. Use horizontal table scrolling for dense operational data, and reserve column hiding for data that is clearly duplicative.

Data Formatting

Use a small display helper instead of formatting inline in every component. The helper should provide consistent rendering for:

  • empty text as -,
  • counts with thousands separators,
  • dates and times in a consistent local or configured format,
  • durations in stable units,
  • metric lookup by name and dimension.

Centralizing formatting prevents visual drift between overview cards, tables, and detail pages.

Security And Redaction

The interface is read-only unless an explicit administrative action is designed. It should not display secrets or raw credential-bearing values.

Apply redaction before values reach Razor components. The UI treats redacted values as normal display text; it does not need to know why a value is hidden. This keeps security policy in the dashboard projection layer rather than in markup.

Replication Checklist

Use this checklist when applying the design to another project:

  • Take colors, fonts, and surfaces from the ZB.MOM.WW.Theme kit tokens; do not define a local color token set.
  • Use the kit's ThemeShell side rail with NavRailSection/NavRailItem and short route labels grouped into sections.
  • Start every page with the same header structure.
  • Put primary numeric state in metric-grid / agg-card cards.
  • Put detailed runtime state in compact responsive tables.
  • Use StatusBadge (kit StatusPill) mapped from real domain states.
  • Use dashed bordered empty states for loading and no-data cases.
  • Use top-bordered sections for page groups instead of nested cards.
  • Centralize formatting and redaction outside Razor markup.
  • Hide every destructive admin affordance from viewers; render it only for the Administrator role and re-check the role server-side on every invocation.
  • Route every destructive action (Close session, Kill worker, Rotate / Revoke / Delete API key) through the shared ConfirmDialog component so the operator always gets one explicit confirmation step before the call reaches the service.