SearchCtrlK

  • ↑↓navigate
  • ↵open
  • escclose

Can’t find it? Contact us

App foundations

What @sdxc/ui/app solves, the rules its parts share for URLs, saving, feedback and keys, and every variable it reads.

The screens of a signed-in product: an app shell, page headers, lists with filters and bulk actions, data tables, dashboards, detail sheets and split views, settings, editors, toasts and a keyboard layer.

It solves the parts every app re-builds by hand, and the judgment behind them: a sidebar that remembers its width, a filtered list that is a link, selection that turns pagination into a bulk bar, saving that is quiet until it fails, and states typed by cause.

Each part renders working server HTML. Small islands add what the platform cannot do: ⌘B, j/k, ⌘K, the bulk bar's count, save timings and toasts.

See the components assembled into whole sites in the demo gallery.

typescript
import "@sdxc/ui/theme.css";
import { … } from "@sdxc/ui/app";

When to reach for it

  • A dashboard, an admin panel, a CMS, an inbox or any screen behind a sign-in.
  • A list people filter, sort, select and act on: FilterBar, Table, SelectionBar.
  • A record opened beside its list (DetailSheet, SplitView) or on its own page (PageHeader, PropertyList, Timeline).
  • Settings and editors: SettingsLayout, SettingsSection, SaveBar, SaveStatus, DangerZone, EditorLayout.
  • Metrics with context: StatCard, Sparkline, StatusStrip, RangePicker, Chart.

Shared ideas

  • State in the URLFilters, sort, view, range and the open record are search params written by GET forms and links, so a view is a link and Back undoes it.
  • Preferences the server rendersThe sidebar's collapse and a split view's widths live in cookies (readAppShellCookie, readSplitViewLayout), so the first paint is right.
  • One selection contractAny list that renders data-selection-item checkboxes works with SelectionBar; Table.Select and Table.SelectAll render them for you.
  • Saving is a modelSettingsSection saves explicit or instant, never both; SaveStatus holds Saving… for 1s; OptimisticToggle flips at once and admits failure.
  • Feedback by placeInline first, Banner for state that needs resolving, Toasts last: one region for server flashes and client toasts, three visible, Undo held 8s.
  • Typed statesEmpty has kind first-run, no-results, cleared, permission and error; Skeleton presets keep the real row and card heights; aria-busy dims a refetching table.
  • One keyboard layershortcutRegistry() ranks modal > sheet > page; data-shortcut registers keys from HTML; KeyboardHelp lists them; CommandPalette opens on ⌘K.
  • No copy in the kitEvery string a part shows or announces is a labels prop, so an app translates them all.
  • Names requiredTable, Tree, GridList, Menubar and the rest warn in development without aria-label.
  • Series by slotcolor={n} picks --ui-chart-<n>, so a legend always matches its series.
  • TargetsDrag handles, resize handles and toast close buttons grow to --ui-target-size.
  • MotionThe rail peek, toast stack, save status and every transition stop under prefers-reduced-motion.

CSS variables

Every custom property the namespace reads or declares, with its default and what it controls.

Reads every role, brand, neutral, success, warning and danger, for fills, borders and toasts, and the raw scales through the chart slots. The roles are documented on Theming.

Charts

The eight slots are declared in theme.css; the rest fall back to the default in the code.

VariableDefaultWhat it controls
--ui-chart-1 … --ui-chart-8brand, success, warning, danger and neutral 600, then brand, success and warning 400; one step lighter in darkThe eight categorical series colors; color={n} picks slot n.
--ui-chart-line-width2pxStroke width of a Chart.Line.
--ui-chart-area-width2pxStroke width of a Chart.Area's outline.
--ui-chart-area-fill-opacity0.25Opacity of the fill under a Chart.Area.
--ui-chart-point-radius0.1875remRadius of every focusable point marker.
--ui-chart-tooltip-gap0.5remSpace between the active point and the tooltip.

Shell and page header

Read by AppShell, PageHeader and every part that sticks under the shell's top bar.

VariableDefaultWhat it controls
--ui-app-sidebar-width16remThe expanded sidebar column.
--ui-app-rail-width3.25remThe collapsed icon rail.
--ui-app-sheet-width18rem, at most 85vwThe sidebar as a phone sheet.
--ui-app-inset-gap0.5remSpace between the frame's edge and the content card.
--ui-app-inset-radius0.75remCorners of the content card.
--ui-app-inset-shadow0 1px 3px rgb(0 0 0 / 0.04)The soft lift under the content card; its border is the card's only edge.
--ui-app-nav-row-size2remHeight of a nav row.
--ui-app-header-height3.5remThe shell's top bar.
--ui-app-content-padding1rem; 1.5rem from 48remPadding of the shell's main region.
--ui-app-sticky-top0px; the shell sets it to its header heightWhere sticky page headers, table headers and the settings nav stick.
--ui-app-gutterset by the shell's main regionIts padding, so a sticky PageHeader can bleed to the card's edges.

Lists and tables

VariableDefaultWhat it controls
--ui-table-row-block-size2.5rem compact, 3.5rem comfortableRow height, set by Table density.
--ui-table-header-block-size2.5remHeader row height.
--ui-table-bgvar(--ui-bg, Canvas)Surface behind the sticky header and pinned cells.
--ui-table-id-max-inline-size14chWidth of a middle-truncated id cell.
--ui-table-bar-min-inline-size6remSmallest width of an in-cell data bar.
--ui-selection-bar-offset1remGap between the floating SelectionBar and the bottom of its scroll box.
--ui-status-dot-size0.5remDiameter of a StatusDot.

Metrics

VariableDefaultWhat it controls
--ui-stat-value-size1.875rem; 1.5rem ruledA StatCard's value.
--ui-stat-card-padding1.25remA StatCard's padding.
--ui-stat-delta-block-size1.375remHeight of the delta pill.
--ui-stat-chart-block-size2.5remHeight of a StatCard's chart slot.
--ui-sparkline-block-size1.5remHeight of a Sparkline.
--ui-sparkline-line-width1.5pxIts stroke.
--ui-sparkline-dot-size, --ui-sparkline-area-opacity0.375rem, 0.16Its last-point dot and area fill.
--ui-status-strip-block-size2remHeight of a StatusStrip bar.
--ui-status-strip-gap2px; 1px under a 30rem containerGap between its bars.
--ui-chart-comparison-color, --ui-chart-comparison-dashneutral muted ink, 4 4A comparison series.
--ui-chart-threshold-width, --ui-chart-threshold-dash1px, 6 4A Chart.Threshold line.
--ui-chart-band-opacity, --ui-chart-band-opacity-dark0.08, 0.12Fill of a Chart.Band in a light and a dark colour scheme.
--ui-chart-band-edge-width, --ui-chart-band-edge-dash1px, 4 3The dashed rules at a Chart.Band's edges.

Detail, split views, settings and editors

VariableDefaultWhat it controls
--ui-app-detail-sheet-widthclamp(28rem, 36cqi, 36rem)Width of a DetailSheet.
--ui-app-detail-sheet-top0pxOffset of a DetailSheet below an app header.
--ui-app-list-width20remDefault width of a SplitView's list pane.
--ui-property-list-label-width9remA PropertyList's label column.
--ui-timeline-icon-box1.5remThe icon box a Timeline's rail runs through.
--ui-settings-nav-width13remThe SettingsLayout nav column.
--ui-settings-measure42remThe settings content column.
--ui-editor-measure42.5remAn EditorLayout's writing column.
--ui-editor-sidebar-width18remAn EditorLayout's properties sidebar.

Feedback and keyboard

VariableDefaultWhat it controls
--ui-toast-peek0.625remHow far each folded toast peeks out of the stack.
--ui-empty-icon-size3rem; 2rem compactAn Empty state's icon well.
--ui-skeleton-list-row-block-size3.5remA Skeleton.List row.
--ui-skeleton-shimmer-duration1.6sOne shimmer sweep.
--ui-app-palette-width40rem, at most the viewport less 1remThe CommandPalette.
--ui-app-palette-top15vhGap above the CommandPalette.
--ui-app-keyboard-help-width40remThe KeyboardHelp sheet.

Trees, panes and toasts

VariableDefaultWhat it controls
--ui-tree-indent1.25remIndent added per level of a Tree row.
--ui-table-body-intrinsic-size32remHeight an off-screen Table.Body reserves while its layout is skipped.
--ui-grid-list-intrinsic-size32remHeight off-screen GridList rows reserve while their layout is skipped.
--ui-skeleton-block-size1remHeight of a Skeleton placeholder.
--ui-resizable-handle-size0.75remThickness of the track a Resizable.Handle reserves.
--ui-resizable-panel-min-size, --ui-resizable-panel-max-sizeset by Resizable.Panel from minSize and maxSize (0% and 100%)Smallest and largest share a pane may take.
--ui-toast-z50Stacking order of the toast region.
--ui-toast-bgset by Toast from color: that role's bg-tintFill of a toast, mixed with transparency behind its blur.

Shared tokens

Read through @sdxc/u's helpers. --ui-target-size and --ui-spacing are declared by the themes; radius and shadow fall back to the default an app overrides by declaring the variable.

VariableDefaultWhat it controls
--ui-target-size1.5rem; 2.75rem on a coarse pointerSmallest pointer target of drag handles, resize handles and a toast's close.
--ui-spacing0.25remUnit of every numeric gap and padding, and of a leaf tree row's gutter.
--ui-text-sm0.875remSize of tree rows.
--ui-radius-md, --ui-radius-lg, --ui-radius-full0.375rem, 0.5rem, 9999pxCorners of skeletons and chart tooltips, toasts, and legend swatches.
--ui-shadow-md0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)Elevation of a chart tooltip.
--ui-shadow-lg0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)Elevation of a toast.

Set by the components

Written by a mixin or from a prop and read back by the part itself. Leave them unset.

VariableDefaultWhat it controls
--ui-chart-tooltip-x, --ui-chart-tooltip-y0px, written by chartTooltip()Where the tooltip sits, from the active point.
--ui-resizable-panel-sizeset from defaultSize; resizeHandle() rewrites it while draggingA pane's share of the group.
@sdxc/ui

Can’t find it? Contact us