@plumile/ui
v0.2.3
Published
Shared React UI primitives and theme for Kronex applications
Maintainers
Readme
@plumile/ui
Shared React UI primitives, layouts, and theme foundations for Kronex applications.
Status
Specialized public package. This package is intended for teams adopting the Kronex design language and component conventions. It is not a generic UI kit for arbitrary React applications.
Purpose
@plumile/ui is the visual layer of the Kronex ecosystem. It provides:
- theme contracts and tokens
- low-level form and feedback primitives
- shared layouts and page shells
- data-heavy and admin-oriented widgets
- reusable backoffice visual components
It does not provide:
- application business logic
- GraphQL orchestration
- router orchestration beyond consuming router links where needed
Installation
npm install @plumile/uiPeer dependencies:
npm install react react-dom i18next react-i18nextIf you use components that depend on typed Plumile router links, also install
@plumile/router. Applications using another router can use the
router-neutral ButtonLink render adapter and AnchorButton.
Main Public Surface
The package entry point groups exports by UI family instead of exposing a single flat mental model.
Theme and styling
ThemeProviderBackofficeThemeProvideruseTheme- theme contracts, tokens, and accessibility helpers
sprinkles,stateSprinkles, and shared style recipes such asfocusRing,controlFocusRing,disabledControl,surfaceInteractive,interactiveControlSurface,interactiveLinkSurface,ghostIconButton,pillLink,entityReferenceLink,insetFocusRing,truncate,iconBox,iconBoxCompact,loadingShimmer, andvisuallyHidden
Primitives and forms
Button,ButtonLink,AnchorButton,LinkButton,Input,Textarea,NativeSelect,CheckboxLabel,Form,FormField,TextareaField,SelectField,FormGroup. The complete fields generate stable IDs and connect descriptions and errors througharia-describedby.TokenFieldprovides a controlled token editor with Enter/comma commit, IME-safe keyboard handling, Backspace removal, focus recovery, and polite add/remove announcements. Applications retain normalization, validation, ordering, and submission ownership throughonTokenCommitand their token state; the draft input is intentionally not serialized as form data.ErrorMessageis the field-level error text primitive. Use it for messages tied to a single control througharia-describedby.FormErroris the canonical form-level error block for submit failures and validation summaries inside form surfaces.InlineBanneris the canonical general notice/banner primitive for non-field feedback. It supports info, success, warning, and danger tones, titles, descriptions, actions, dismiss controls, and explicit live-region overrides. Its opt-infocusOnMountcontract supports interruptive error summaries without rebuilding focus behavior in applications.EmptyStateis the canonical empty-state component. Usevariant="framed"for card-backed surfaces,variant="plain"for inline backoffice-style surfaces,density="compact"for dense lists, andtonefor neutral, muted, info, success, warning, or danger emphasis.LoadingStateandErrorStateprovide canonical page, panel, and lazy boundary presentation. Both support plain or framed rendering, compact density, alignment, and long descriptions;ErrorStatecan render a standard retry action without owning the retry lifecycle.ErrorBoundarycontains render failures with a node or render-function fallback, reset callbacks, andresetKeyrecovery.AsyncBoundarycomposes that contract withSuspensefor lazy and Relay-adjacent views without owning data fetching or product telemetry.ToastProvider,ToastViewportProgressBarexposes bounded progress semantics and optional visible value text.PaginationControlsprovides labelled previous/next navigation for cursor- and page-based lists.SliderFieldandNumberSteppercover labelled bounded numeric controls; applications provide localized value and action labels while the library owns layout, focus, disabled states, and native form participation.
Dialogs and action menus
Modalpreserves the established controlled API while delegating modal focus confinement, outside inertness, Escape dismissal, scroll locking, and focus restoration to Base UI. UseinitialFocusfor the entry strategy andfinalFocusRefonly when a workflow has an explicit return target.Drawerapplies the same focus, dismissal, scroll-lock, and restoration contract to a side panel positioned at the start or end of the viewport.ConfirmDialogrenders an interruptivealertdialog, focuses its safe cancel action first, supports async confirmation/error states, and acceptsreturnFocusReffor explicit focus restoration.DropdownMenuis the canonical flat action menu. It provides roving focus, disabled items, typeahead, collision handling, and default/danger tones. Keep grouped navigation menus local until the public contract exposes semantic groups and router-native link items.
Base UI is an internal behavior dependency. Consumers import the styled
Plumile contracts above, not @base-ui/react directly.
Layouts and shells
Stack,Inline,Grid, andContainerprovide the token-based layout foundation for vertical rhythm, wrapping horizontal groups, fixed or auto-fitting grids, and centered content widths. Their nativedivattributes and refs are forwarded without a polymorphic generic API.Headingrenders an explicit semantic level fromh1throughh6, with optional token-based visual size, weight, tone, and alignment.Textrenders a paragraph by default and supports the simplep,span, anddivtext roots.PageShell,AdminShellLayout,Sidebar,ProfileDropdown(intended for the sidebarfooterslot)Surfaceis the canonical structured surface primitive for titled content, optional descriptions, actions, body content, and footers. It supportsdiv,section, andarticleroots plus visual variants for brand, neutral, muted, and plain surfaces.SurfaceSectionwrapsSurfacefor accessible titled sections and wiresaria-labelledbyautomatically when a title is present.ContentLayoutsupports optionalcontentWidth(fullornarrow),fillHeight,bodyScroll, and atoolbarslot for list-oriented pages.SplitRowListandSplitRowListItemrender a two-column list row with sidebar-like hover and focus treatment. ExportsplitRowListStyleswhen a page needs the sharedprimaryLinkclass.PropertyListis the neutral semantic definition-list pattern for product and record metadata. It supports split, stack, and responsive grid layouts, plain or surfaced rows, and explicit empty-value labels.ResizableSplitView,PanelShellPageHeaderis the generic page heading composition for optional leading, description, metadata, and action slots.BackofficePageHeaderremains the compatibility adapter for its narrower historical API.ResourceDetailHeaderpresents a resource identity, compact metadata, semantic status indicators, and explicit actions. Applications own business tone mapping and provide translated labels for the status and action groups; use the slot-basedDetailSummaryHeaderfor a subordinateh3summary.PageShellRuntimeProvider,usePageShellRuntime,usePageShellContentClassName, andusePageShellSidePanelprovide optional runtime state for layouts where descendant pages register a content class or side panel while the shell stays owned by a parent layout.ResizableSplitViewis the canonical split-view primitive for optional, resizable side panels. It supports controlled and persisted widths, start/end panel placement, direction-correct pointer and keyboard resizing, stacked layouts below a breakpoint, and accessible separator semantics.Toolbar,ToolbarGroup,ToolbarSpacer,ToolbarSeparatorStepIndicatorrenders a semantic ordered list for known multi-step flows, including current and completed states; applications retain step labels, navigation, validation, and workflow state. IfcurrentIdis absent from a non-empty item set, the first step is the explicit current-step fallback.Disclosurewraps nativedetails/summarysemantics for optional content, with plain or outlined presentation and compact or regular density.CompactMetadataListandCompactMetadataListItemprovide a wrapping semantic list for terse labels with optional supporting metadata.SettingsLayoutfor settings-oriented content with local navigation. Itssurfaceandflushpresentations and static/sticky sidebar policy cover both framed settings cards and full-height application settings.SegmentedControlis the mandatory single-selection control for display modes and view preferences. UseTabsinstead when each choice owns a tabpanel.Card,PanelShell,ActionPanel, andBackofficeFormSectionremain compatible wrappers or specialized compositions on top of the canonical surface primitives.
Router destinations
Components that render router links accept a structured
RouterLinkDestination alongside their legacy href or to field:
const closedTasks = {
pathname: '/tasks',
filters: { view: { eq: 'closed' } },
query: { panel: 'summary' },
} satisfies RouterLinkDestination;
<Tabs
items={[{ id: 'closed', label: 'Closed tasks', destination: closedTasks }]}
/>;Structured destinations are supported by tabs, sidebars, breadcrumbs,
PageShell, relation menus, dashboard links, and interactive InfoTile
instances. Their filters are serialized with the destination route schema by
@plumile/router; explicit to.search still takes precedence. LinkButton
already forwards all router Link props and exports LinkButtonProps for
wrappers that need the same contract.
Data-heavy and dashboard components
DataTableis the canonical high-level table for static and paginated data.DataTableRoot,DataTableHeader,DataTableBody,DataTableRow, andDataTableEmptyRowexpose the same public rendering primitives for advanced table composition without duplicating cell, header, density, breakpoint, empty state, or contained-scroll behavior.VirtualizedConnectionTableis the specialized table for virtualized connection lists and infinite loading. It shares theDataTableprimitives for table rendering while keeping virtualization and scroll-trigger behavior separate from the base table API.ResponsiveRecordListadapts the same table columns between table and mobile card layouts.renderActionsupplies one action column across both presentations unless the caller already declares anaction/actionscolumn;actionColumnLabeloverrides its translated header.TechnicalIdentifierValueandTechnicalIdentifierListnormalize, abbreviate, display, and copy opaque identifiers. Fingerprints retain their leading and trailing groups; empty values render nothing.CopyableTextis the generic copy-with-feedback primitive used by technical identifiers, metrics, and backoffice compatibility facades.BackofficeDataTablecomposes selection, bulk actions, and toolbar behavior on top ofDataTable; virtualized backoffice lists useVirtualizedConnectionTabledirectly.MetricCardandMetricTileGroupare the canonical KPI primitives. They support metric tones, compact density, copyable values, and configurable responsive tile widths without changing their default rendering.DashboardPanel,DashboardMetricGroup,DashboardQuickActions, andDashboardStatusListprovide the canonical visual widgets for dashboard panels, linked metric groups, shortcut lists, and status summaries.StatusSummaryPanelActivityFeed,TimelineEventRow,InfoTileTimelineEventRowis the reusable dashboard timeline row primitive. It uses the canonical event vocabulary (title,timeortimestamp,description,meta,metrics,details, and semantictone) while keeping relative timestamp rendering available throughRelativeTimeText.
Formatting helpers
HighlightCodeis the general code-display primitive. It supports a badge, copy action, placeholder text, highlighted HTML/React content, and a plain preformatted fallback.MarkdownRendereralways disables raw HTML parsing and supports explicit image policies: render images, keep only their alternative text, or omit them. Applications can therefore render text-only legal and policy content without maintaining a second Markdown component.FormActionsowns the wrapped, trailing form-action layout and supports both submit-only and submit-with-cancel forms, including the shared button loading treatment.copyTextToClipboard,copyRichClipboard, anduseClipboardCopyprovide shared clipboard behavior with rich multi-format writes where browsers support them and text fallbacks everywhere else.BackofficeJsonVieweris the canonical structured JSON viewer for backoffice surfaces. It safely stringifies unknown values, exposes copy and collapse/expand controls, and accepts max-height and label overrides.BackofficePayloadVieweris the canonical backoffice payload viewer forjson,markdown,text, andcodepayloads. Use it inside detail sections or tool output surfaces for empty states, summaries, collapsible details, copyable text/code, markdown rendering, and JSON rendering throughBackofficeJsonViewer.MoneyAmountrenders a currency amount fromamountandcurrencyprops with fixed two-decimal formatting.formatCurrencyAmount({ amount, currency })provides the same formatting for plain-text usage such as charts, table configs, and tooltips.buildUtcDayRange,toUtcDayKey,toUtcDailyCategorySeries, and the mapped time-series helpers provide generic UTC day bucketing and zero-filled chart series without embedding product category names or billing vocabulary.usePersistentBooleanStatepersists boolean UI preferences such as collapsed panels or sidebars while staying SSR-safe and storage-key agnostic.
Backoffice visual components
Badgeis the low-level inline visual marker. It supports neutral, info, success, warning, danger, and accent tones plus a loading shimmer.StatusBadgemaps semantic status tones to badge tones and can show an optional status dot.Tagis the compact pill component for short labels and optional removable filters. Usetitle,onRemove, andremoveLabelfor accessible removable tags.EnvironmentBadgeandBackofficeIdBadgeremain specialized public badge compositions for environment labels and copyable IDs.- Detail badge/value compositions are available for backoffice entity pages:
BackofficeStatusMetaBadge,BackofficeStatusGroup,BackofficeDetailFlagTag,BackofficeDetailTaggedValue,BackofficeReferenceValue, andBackofficeEnumLabel. BackofficeKeyValueListis the canonical definition-list renderer for backoffice label/value metadata. It supports surface or plain rendering, split, stack, and grid layouts, custom empty labels, and React node values.- spinners and skeletons
BackofficeToolbaris the canonical list/table toolbar for search, filters, sorting, actions, and active chips.BackofficeListFilterToolbarcomposes that toolbar with the canonical filter trigger, active chips, searchable drawer, reset behavior, and keyed session-scoped drawer state. Applications provide filter sections and own only their domain filter values.BackofficeListFooteris the canonical infinite-list footer for status-only loading flows. Its discriminatedstatecovers idle, loading, completion, and retryable incremental errors. Keep load-more status, loaded counts, spinners, and manual load buttons there rather than rebuilding list footers.InfiniteScrollTriggerobserves the viewport or an explicitscrollRootRef, keeps a manual keyboard-accessible action, and announces loading and list completion. UseModal.scrollRootReffor collections inside modal content.BreadcrumbNavigationmenus accept either static searchableitemsor a composedcontentelement. Composed content mounts only while the dropdown is open, so applications can own lazy data loading and pagination without blocking their page or navigation shell.BackofficeEmptyStateis a compatibility wrapper aroundEmptyStatewith the plain, compact backoffice defaults.BackofficePickerShell,BackofficePickerList, andBackofficePickerRoware the canonical visual primitives for searchable backoffice pickers. They own the search input layout, listbox/option rendering, empty state, and row truncation; application integrations should pass already-resolved picker items and keep manifest, Relay, and domain behavior outside@plumile/ui.- filter drawers and confirm dialogs stay separate from picker primitives because their navigation, action, and confirmation semantics differ.
- backoffice data-table wrappers and detail visual helpers, including
BackofficeDetailFieldandBackofficeDetailSectionfor generic titled detail sections and copyable field values, plusBackofficeScopeStackandBackofficeAuditMetadataPanelfor canonical scope/context and audit metadata sections AuditTimelineis the canonical visual timeline for backoffice lifecycle and audit history. It renders orderedol/lievents with title, time, description, actor, source, meta, metrics, details, density, customrenderEvent, and neutral/info/success/warning/danger tones. Empty timelines rendernullunlessemptyStateis supplied.DetailPageTemplate,DetailPageTemplateContent, andListPageTemplateare the canonical backoffice page templates for detail and list screens.Sidebar, the canonical navigation rail with two states (expandedandcollapsed), unified light/dark tokens for public and backoffice surfaces, a fixed Kronex brand header with a collapse control, optionalsearchandfooterslots, collapsible section groups, and optional sortable sections for backoffice navigation.SidebarNavigationremains as a deprecated alias.
Breaking changes in this line: BackofficeTableToolbar, TableToolbar,
BackofficeFiltersBar, BackofficeLoadMore,
BackofficeInfiniteListStatus, BackofficeVirtualizedConnectionTable,
BackofficeDetailLayout, DetailPage, DetailPageContent, SegmentedNav,
and SectionNav were removed in favor of the canonical components above and
Tabs.
For the complete export list, see src/index.ts.
Quick Start
import { Button, ThemeProvider, Toolbar, ToolbarGroup } from '@plumile/ui';
export function ExampleScreen(): JSX.Element {
return (
<ThemeProvider defaultMode="light">
<Toolbar ariaLabel="Screen actions">
<ToolbarGroup>
<Button>Save</Button>
</ToolbarGroup>
<ToolbarGroup justify="end">
<Button variant="secondary">Cancel</Button>
</ToolbarGroup>
</Toolbar>
</ThemeProvider>
);
}Foundation composition stays explicit and uses the shared spacing and type scales:
import { Container, Grid, Heading, Stack, Text } from '@plumile/ui';
export function ProjectSummary(): JSX.Element {
return (
<Container size="5xl">
<Stack space="lg">
<Heading level={1}>Project summary</Heading>
<Text tone="secondary">Current delivery signals.</Text>
<Grid columns="auto">
<div>Build</div>
<div>Tests</div>
<div>Release</div>
</Grid>
</Stack>
</Container>
);
}ThemeProvider stores the public shell preference in plumile-theme-mode.
BackofficeThemeProvider stores the backoffice preference separately in
plumile-backoffice-theme-mode, so a backoffice can resolve light, dark, or
system mode independently inside a public page.
For embedded or nested surfaces, compose ThemeModeProvider with ThemeScope;
this keeps mode state independent and scopes theme variables to the wrapper.
DocumentThemeClass is the explicit adapter for applications that need the
selected class on body or documentElement. All three accept controlled or
uncontrolled modes, a storage key, and an optional caller-owned storage adapter.
Custom theme pairs use the public vanilla-extract contract:
import { createTheme } from '@vanilla-extract/css';
import {
vars,
type ThemeClassPair,
type ThemeValues,
} from '@plumile/ui/theme/index.js';
const lightValues = {/* complete token tree */} satisfies ThemeValues;
const darkValues = {/* complete token tree */} satisfies ThemeValues;
export const productTheme = {
light: createTheme(vars, lightValues),
dark: createTheme(vars, darkValues),
} satisfies ThemeClassPair;Package Layout
theme/*canonical source of shared design tokens and theme primitivesatomic/*low-level reusable React building blockscomponents/*generic cross-application layouts and widgetsbackoffice/*specialized data-heavy visual components for admin and backoffice screens
Public Family Owners
The inventory guard derives these families from public source owners. The matching Storybook navigation is the visual catalogue; focused tests remain the behavioral owner. The comments are stable machine-readable reconciliation keys.
| Family | Discoverable owner |
| -------------------------------------------- | ------------------------------------------------------------------------- |
| Actions | Atomic/Atoms and action-group stories |
| Backoffice | Backoffice and Admin stories |
| Data display | table, record-list, tile, metric, timeline, and chart stories |
| Feedback | banner, empty, toast, spinner, skeleton, and status stories |
| Forms | input, checkbox, radio, switch, select, and field stories |
| Icons | generated icon exports and icon catalogue stories |
| Layout | foundation, shell, surface, card, and panel stories |
| Navigation | navigation tabs, interactive tabs, breadcrumbs, sidebar, and menu stories |
| Overlay | modal, dialog, popover, and preview stories |
| Theme | public/backoffice theme provider stories |
| Typography | heading, text, Markdown, and code stories |
| Utilities | remaining focused public helpers and their owner stories |
Validation Notes
- public UI behavior should be covered by targeted tests for stable components
- visual breadth is large, so documentation and examples should stay organized by component families rather than exhaustive component-by-component prose
- Storybook stories are useful for visual review, but they do not replace behavioral tests for stable public components
npm run ui-css:checkfrom the repository root records and enforces the published stylesheet's raw, gzip, and rule-count budgets after a package buildnpm run ui-contracts:auditemits the root export, known-consumer, and local owner-evidence matrix as JSON;npm run ui-contracts:checkalso rejects consumer imports absent from the public root contractnpm run ui-sprinkles:auditemits every known Sprinkles helper, property, responsive condition, and unresolved dynamic call as JSON;npm run ui-sprinkles:checkverifies that the audit remains executable- styling should prefer shared sprinkles and theme style recipes; raw
vanilla-extract
style()is reserved for selectors, pseudo-elements, keyframes, container queries, CSS variables,calc(),color-mix(), or highly specific grid/layout cases - shared UI styling is sprinkles-first: add recurring token-sized values and
reusable interactive/link/icon classes to
theme/styleRecipes.css.tsbefore creating local one-offstyle()blocks
Limitations
- this package assumes Kronex theme tokens and styling conventions
- it is intentionally opinionated for backoffice and admin-style interfaces
- it does not try to be a framework-agnostic design system
- some components assume other Kronex packages, especially
@plumile/router
