Frontend Design System
The web console uses repository-owned shadcn/ui components. Component source generated from the current shadcn registry stays inside the application, interactions use the component’s Radix primitives, and variants are explicit TypeScript APIs instead of copied class-name strings. The SENS semantic tokens and visual language remain authoritative. The system does not introduce a hosted service or a separate runtime.
Boundaries
Section titled “Boundaries”The design system owns presentation and local interaction behavior. It does not make authorization decisions, infer tenant scope, or hide API errors. Product views still own their domain data, permissions, labels, and table columns.
Reusable code lives in:
apps/web-console/src/components/ui/for small primitives and layout compositions;apps/web-console/src/components/ui.tsxfor domain-neutral feedback, overlays, icons, and existing compatibility exports;apps/web-console/src/styles.cssfor semantic tokens and component styles;apps/web-console/components.jsonfor the shadcn CLI contract;apps/web-console/src/components/design-system-showcase.tsxfor the local component catalogue.
Only extract a component when the same intent occurs at least three times. Similar-looking elements with different operational meaning stay separate.
Visual foundations
Section titled “Visual foundations”| Token role | Reference value | Usage |
|---|---|---|
| Signal Blue | #1769FF | Primary actions, focus, current selection |
| Telemetry Teal | #007F78 | Healthy telemetry and active signal paths |
| Control Navy | #0B1724 | Navigation and high-contrast operational surfaces |
| Workspace Fog | #F3F6F7 | Application background and quiet grouping |
| Warning Amber | #A86400 | Recoverable warnings and incomplete states |
| Critical Red | #B42332 | Destructive actions and failed states |
Display headings use the condensed system stack, body copy uses the platform UI stack, and identifiers, timestamps, checksums, and telemetry values use the monospace stack. Monospace is not used merely as decoration.
Component inventory
Section titled “Component inventory”| Component family | Import | Purpose |
|---|---|---|
Button | components/ui/button | Semantic variants, sizes, icons, disabled and busy states |
Badge | components/ui/badge | Compact neutral, informational, success, warning, or danger labels |
Input, Textarea | components/ui/input, components/ui/textarea | Text entry with shared focus, validation, and disabled styling |
Select, SimpleSelect | components/ui/select | Radix-backed option selection; SimpleSelect is the typed application composition |
Checkbox, Label | components/ui/checkbox, components/ui/label | Accessible binary input and explicit field labelling |
Table family | components/ui/table | Semantic operational tables with bounded horizontal overflow |
Tabs family | components/ui/tabs | Keyboard-accessible switching between equal-level views and filters |
Card, Alert | components/ui/card, components/ui/alert | Shared content and feedback surfaces used by application compositions |
Dialog, AlertDialog | components/ui/dialog, components/ui/alert-dialog | Focus, Escape, and modal semantics for overlays and confirmations |
Sidebar family | components/ui/sidebar | Responsive primary navigation backed by Sheet, Tooltip, Separator, and Skeleton primitives |
AppSidebar compositions | components/app-sidebar, tenant-switcher, nav-main, nav-user | Official collapsible sidebar-07 structure connected to scoped tenants, navigation, API status, and GET /v1/auth/me |
Breadcrumb family | components/ui/breadcrumb | Accessible Platform → tenant → current view context in the SidebarInset header |
Skeleton, LoadingState | components/ui/skeleton, components/ui.tsx | Shape-preserving loading feedback for navigation, pages, tables, and modal details |
DataPanel | components/ui/layout | Shared boundary for loading, empty, error, and tabular data |
DataToolbar, InventoryToolbar | components/ui/layout | Search, filter, and result-count compositions |
TablePagination | components/ui/layout | Accessible previous/next cursor navigation, current-page status, and bounded page-size selection |
LoadMoreActions | components/ui/layout | Progressive cursor loading for non-tabular feeds and timelines |
DialogActions | components/ui/layout | Sticky modal footer with normal or split actions |
EditorDialog, ConfirmDialog | components/ui.tsx | Centered application modals over shadcn Dialog and Alert Dialog |
StatusChip and feedback states | components/ui.tsx | Loading, empty, success, status, and diagnostic error states |
Import concrete component files where possible. Avoid a new application-wide barrel because it increases coupling and can make browser bundles harder to analyse.
The application shell follows a fresh shadcn sidebar-07 registry composition:
SidebarProvider contains AppSidebar and SidebarInset; the sidebar header
contains the controlled tenant switcher, the content contains role-filtered
navigation, and the footer contains API status plus the current /me identity.
The inset header uses the shadcn breadcrumb. Responsive, mobile-sheet, and
collapsed behavior belongs to the shadcn primitive. Do not reproduce those
states with breakpoint-specific width or visibility overrides.
Editing workflows use centered EditorDialog modals. They must not be styled
as edge-attached drawers. Initial content loads use LoadingState skeletons
whose shape reflects the surrounding page, table, or detail surface. Busy
buttons remain explicit action progress indicators so their labels and layout
do not disappear during a mutation.
Portal layers use one explicit stack: modal and mobile-sheet backdrops are at layer 80, their interactive content is at layer 90, and portalled Select, Dropdown Menu, and Tooltip content is at layer 100. A backdrop and its modal must never share the same layer; otherwise the backdrop can obscure the focused content in a browser stacking context.
Local catalogue
Section titled “Local catalogue”Run the web console locally and open:
http://127.0.0.1:5173/design-systemThe route exists only in Vite development mode. Production builds remove the catalogue branch, and the page is not part of product navigation or an authorization contract.
Adding components
Section titled “Adding components”The application is configured for the shadcn CLI. A new component can be
inspected with the current CLI and added from apps/web-console:
pnpm dlx shadcn@latest add <component>Treat generated code as an editable starting point. Keep direct imports from the individual Radix packages instead of adding an aggregate runtime barrel. Before merging:
- map colors, radii, focus, and typography to existing semantic tokens;
- keep German product copy accurate and UTF-8 encoded;
- use Radix only for interactions that need its accessibility behavior;
- verify keyboard focus, Escape behavior, reduced motion, empty, error, loading, and disabled states;
- migrate all equivalent existing uses and remove superseded markup;
- add interaction tests and update this inventory.
Do not use the CLI to apply a preset over the existing theme. The visual world is specific to the SENS telemetry operating context and must not be replaced by a generic preset as a side effect of adding one component.
Security and tenancy
Section titled “Security and tenancy”UI visibility is convenience only. platform-api remains the authorization
boundary. Reusable components must receive already-scoped data and must not
cache tenant-owned data, infer tenant identifiers, or turn disabled controls
into authorization checks. Diagnostic components may render correlation IDs
but never tokens, credentials, payload bytes, or cross-tenant details.
Radix overlay positioning and responsive layout variables require inline style
attributes. The static server permits only that attribute-level behavior via
style-src-attr 'unsafe-inline'. A per-response nonce authorizes the Radix
scroll-lock style element; external style origins and inline scripts remain
restricted by the rest of the Content Security Policy.
The Flow Studio mounts modal Dialog.Content only while Focus mode is active.
Its ordinary workspace therefore remains in the accessibility tree without
dialog semantics; opening traps focus inside the workspace, and Button or
Escape closure restores focus to the focus-mode trigger button. Controlled
graph state stays outside the modal wrapper so unsaved edits survive the
transition.
Verification
Section titled “Verification”For design-system changes, run the web-console typecheck, focused component and view tests, the production build, and the Impeccable detector. Verify the local catalogue at desktop and mobile widths. Changes to a domain workflow still need the tests required by that module in addition to these UI checks.