Dashboard Components

@sero-ai/ui provides shared dashboard components for compact widgets and plugin views. Use them for consistent spacing, text, status colours, and overflow behaviour.

Import everything from the package root:

import { WidgetContent, Stack, Metric, Status, ItemList } from '@sero-ai/ui';

The root is for primitives, dashboard components, hooks and theme utilities. Import AI elements from their stable subpaths so plugins only load the editor and rendering dependencies they use:

import { Message } from '@sero-ai/ui/ai-elements/message';

Federated plugins that use AI elements should also import their Tailwind source entry after the base plugin stylesheet. The same rule applies to model-selection and context-editor components:

@import "@sero-ai/ui/styles/plugin.css";
@import "@sero-ai/ui/styles/ai-elements.css";
@import "@sero-ai/ui/styles/model-selection.css";
@import "@sero-ai/ui/styles/context-editor.css";

Only import the specialized stylesheets for component families the plugin uses.

Detailed props are canonical in the exported TypeScript types. The machine-readable catalogue (the data behind the tables below) ships as plain JSON at a stable subpath — read it directly:

@sero-ai/ui/dashboard-catalog.json

Worked, glass-styled example widgets ship on their own subpath — import them to preview, or copy one as a starting point:

import { StarterExample, SchedulerExample } from '@sero-ai/ui/reference';

In the tables, primitive means an existing @sero-ai/ui primitive that is suitable for widgets. Composite means a dashboard component.

Layout

ComponentKindPurposeUse when
WidgetContentcompositeFull-height widget frame with padding + container-query boundaryOutermost element of a widget
StackcompositeVertical layout: gap, alignment, optional scrollPrimary top-to-bottom flow
InlinecompositeHorizontal layout: gap, align, wrap, justifyA row, e.g. header + action
GridcompositeResponsive grid with bounded columnsMetric / summary layouts
SectioncompositeCompact section: heading, description, actionGroup content under a label
DividercompositeCompact divider on SeparatorSeparate sections

Typography

ComponentKindPurposeUse when
TextcompositeSemantic variants (body/label/supporting/muted/numeric) + truncationAny text
HeadingcompositeCompact heading levels/sizes, correct HTML semanticsTitles
IconcompositeIcon wrapper for size, tone, accessibilityRender an icon consistently

Data display

ComponentKindPurposeUse when
Metric / MetricCard / MetricGroupcompositeLabel + value, contained card, responsive groupNumbers and summaries
StatuscompositeSemantic dot / label / pillHealth / state indicator
BadgeprimitiveCounts, tags (gained success/warning/info tones)A count or tag
KeyValue / KeyValueListcompositeAligned label/value metadataConfig, totals, metadata
ItemList / ItemListItemcompositeCompact rows with media/text/metadata + overflowEntity lists
ActivityList / ActivityListItemcompositeChronological events + overflowTime-ordered activity
ProgressRingcompositeAccessible circular gauge, no charting depA bounded value / rate
StaleIndicatorcomposite"Data may be stale" hint + optional refreshData can lag its source

Runtime states & feedback

ComponentKindPurposeUse when
DataBoundarycompositeSelects presentation for loading/ready/empty/stale/errorSwitch by data state
EmptyStatecompositeCompact empty stateNothing to show yet
AlertprimitiveInline warning / errorPersistent inline message
MetricSkeleton / ListSkeleton / ActivitySkeletoncompositeLoading placeholdersloading fallbacks
SkeletonprimitiveBespoke loading shapesPrebuilt patterns don't fit

Actions

ComponentKindPurposeUse when
ButtonprimitiveStandard text actionA labelled action
IconButtoncompositeIcon-only button, required accessible labelCompact action (refresh, dismiss)

Boundary: shared vs plugin-local

Shared dashboard components are presentation only — they hold no plugin state, actions or domain types. Keep domain-specific components and types in the plugin. Add a component to @sero-ai/ui only when the pattern is reused across more than one widget or plugin.

Sizes and overflow

Widgets stay legible at their declared minimum and default sizes using CSS and container queries. Provide compact defaults; let the plugin choose which data to show at each size. Use Stack's scroll and the list components' overflowCount for "+N more" rather than hand-rolling overflow.

Loading, empty and error

Wire runtime states through DataBoundary, with a skeleton pattern for loading, EmptyState for empty, and Alert for error. The boundary selects presentation; the plugin owns fetching and retry.

Glass styling

WidgetContent applies the dashboard glass token scope. Do not add glass classes or backdrop-filter. The host tile provides the blur.

For a full plugin view that should stay solid rather than glass, opt out with <WidgetContent glass={false}>. Form controls and portalled menus stay solid inside glass regardless, so inputs and dropdowns remain readable.

Styling setup

An external plugin's ui/styles.css must import the shared stylesheet and scan its own files:

@import "@sero-ai/ui/styles/plugin.css";
@source "./**/*.{ts,tsx}";

plugin.css already scans the dashboard components, so their utility classes are emitted for you. Import ../styles.css from every directly-exposed Module Federation entry.

Sero wraps contributed components in PluginStyleScope. Use the shared stylesheet and the seroPluginCssScope Vite helper so document-level Tailwind selectors stay inside this scope. Sero's design tokens inherit from the host.

Contributing a reusable component

For an in-repository contribution, add a reused pattern under packages/ui/src/components/dashboard/. Build it from existing primitives and design tokens. Export it from the package root, and add it to the catalogue.