StatCard
Stat cards for a dashboard.
View code
<StatCard.Group aria-label="Last 7 days">
<StatCard>
<StatCard.Label>Uptime</StatCard.Label>
<StatCard.Value>99.98%</StatCard.Value>
<StatCard.Delta trend="up" trendLabel="Up" comparison="vs last week">+0.02%</StatCard.Delta>
<StatCard.Footer>One 4-minute outage on Tuesday</StatCard.Footer>
</StatCard>
<StatCard>
<StatCard.Label>p95 latency</StatCard.Label>
<StatCard.Value>182 ms</StatCard.Value>
<StatCard.Delta trend="up" invert trendLabel="Up" comparison="vs last week">+12 ms</StatCard.Delta>
<StatCard.Chart>
<Sparkline values={latency} label="p95 latency, rising over the last 12 hours" area tone="warning" />
</StatCard.Chart>
<StatCard.Footer>Slowest in eu-west since the 14:02 deploy</StatCard.Footer>
</StatCard>
<StatCard>
<StatCard.Label>Error rate</StatCard.Label>
<StatCard.Value>0.4%</StatCard.Value>
<StatCard.Delta trend="down" invert trendLabel="Down" comparison="vs last week">−0.3%</StatCard.Delta>
<StatCard.Footer>Mostly 502s from the billing API</StatCard.Footer>
</StatCard>
<StatCard>
<StatCard.Label>Open incidents</StatCard.Label>
<StatCard.Value>2</StatCard.Value>
<StatCard.Delta trend="flat" trendLabel="No change" comparison="vs yesterday">0</StatCard.Delta>
<StatCard.Footer>Both on checkout, oldest 3h 12m</StatCard.Footer>
</StatCard>
</StatCard.Group>Demo apps
Whole pages built only from @sdxc/ui, each for an invented product with its own palette and type. They open in a new tab.
- CMSInkwellInkwell, a demo CMS built from @sdxc/ui/app: posts with count tabs and quick edit, an editor with a quiet save status and a loud Publish, media and settings./demo/apps/inkwell
- Ops dashboardPulsePulse, an invented ops dashboard: Northwind's services with live filters, a peek sheet and a bulk bar, metrics that switch one chart, incidents, deploys and settings./demo/apps/pulse
- Support inboxRelayRelay, an invented support inbox: mailboxes, conversations and the open conversation on one screen, with contacts, reports and settings./demo/apps/relay
Every demo, landings and docs sites alike, is listed in the demo gallery.
Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/ui remiximport "@sdxc/ui/theme.css";Usage
import { StatCard } from "@sdxc/ui/app";Renders one stat: a grid that sets the label and delta on one line, the value under them,
then the trend and the footer, in that order whatever order the parts are written in. With
href the whole card is one link, and current marks it as the metric being shown.
Composition
The parts the component publishes, each a static property of the host.
StatCardStatCard.GroupLays stats out by the width of their container: one column, two compact ones from 18rem, two full-size from 28rem, andcolumnsfrom 52rem.StatCard.LabelRenders the stat's name in muted 14px text at the start of the first line.StatCard.ValueRenders the number at--ui-stat-value-size(30px in a card, 24px in ruled columns, 22px in a compact group), semibold in tabular figures, so values in a row line up and a live update never shifts its width.StatCard.DeltaRenders the change as a pill toned by meaning (success when it moved the good way, danger when the bad way, neutral when flat) with a decorative arrow, then the comparison in muted text.StatCard.ChartHolds a trend under the value, such as a Sparkline, at--ui-stat-chart-block-sizeacross the card's full width.StatCard.FooterRenders the sentence that explains the number ("2 monitors down since 09:14"), muted, under everything else.StatCard.SkeletonStands in for a stat while it loads: the same parts on the same lines, each holding a placeholder bar, so the card keeps its height when the real one streams in.
Examples
Ruled columns
View code
<StatCard.Group variant="ruled" aria-label="This week">
{STATS.map((stat) => (
<StatCard key={stat.label}>
<StatCard.Label>{stat.label}</StatCard.Label>
<StatCard.Value>{stat.value}</StatCard.Value>
<StatCard.Delta
trend={stat.trend}
invert={stat.label !== "Checks run"}
comparison="from last week"
>
{stat.delta}
</StatCard.Delta>
</StatCard>
))}
</StatCard.Group>Cards as the chart's tabs
View code
<div mix={[vstack({ gap: 3, align: "stretch" })]}>
<StatCard.Group variant="tabs" columns={3} aria-label="Metric shown in the chart">
<StatCard href="/examples/dashboard?metric=requests" current>
<StatCard.Label>Requests</StatCard.Label>
<StatCard.Value>610k</StatCard.Value>
<StatCard.Delta trend="up" comparison="vs yesterday">+8%</StatCard.Delta>
</StatCard>
<StatCard href="/examples/dashboard?metric=errors">
<StatCard.Label>Errors</StatCard.Label>
<StatCard.Value>2,431</StatCard.Value>
<StatCard.Delta trend="up" invert comparison="vs yesterday">+14%</StatCard.Delta>
</StatCard>
<StatCard href="/examples/dashboard?metric=latency">
<StatCard.Label>p95 latency</StatCard.Label>
<StatCard.Value>182 ms</StatCard.Value>
<StatCard.Delta trend="down" invert comparison="vs yesterday">−6 ms</StatCard.Delta>
</StatCard>
</StatCard.Group>
<Chart width={480} height={140} xDomain={[0, 11]} yDomain={[0, 80]} aria-label="Requests per hour, last 12 hours">
<Chart.Area points={requests} />
</Chart>
</div>Loading
View code
<StatCard.Group aria-label="Loading stats" aria-busy="true">
<StatCard.Skeleton />
<StatCard.Skeleton chart />
<StatCard.Skeleton />
<StatCard.Skeleton />
</StatCard.Group>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Required | Description |
|---|---|---|---|
href | string | No | Makes the whole card a link, such as the metric's chart tab or its detail page. |
current | boolean | No | Marks a link card as the metric the page shows, through aria-current="true". |
children | RemixNode | Yes |
Also accepts everything in Omit<TagProps<"div">, "children">.
StatCard.Group
| Prop | Type | Required | Description |
|---|---|---|---|
variant | "cards" | "ruled" | "tabs" | No | |
columns | number | No | Columns at full width; a narrower container shows at most two, compact, then one. |
Also accepts everything in TagProps<"div">.
StatCard.Label
StatCard.Value
StatCard.Delta
| Prop | Type | Required | Description |
|---|---|---|---|
trend | "up" | "down" | "flat" | Yes | |
invert | boolean | No | Tones a fall as good and a rise as bad, for numbers such as latency or error rate. |
trendLabel | string | No | Spoken before the change, since the arrow is decorative ("Increased by"). |
comparison | RemixNode | No | The period it compares with, set after the pill ("vs last week"). |
Also accepts everything in TagProps<"span">.
StatCard.Chart
StatCard.Footer
StatCard.Skeleton
| Prop | Type | Required | Description |
|---|---|---|---|
delta | boolean | No | Reserves a delta pill. |
chart | boolean | No | Reserves the trend slot. |
footer | boolean | No | Reserves one footer line. |
Also accepts everything in Omit<TagProps<"div">, "children">.
Types
| Name | Values | What it decides |
|---|---|---|
Variant | "cards" | "ruled" | "tabs" | How a group sets its stats: cards boxes each one, ruled sets columns under a 1px rule, and tabs joins link cards into one divided strip whose current card underlines. |
Trend | "up" | "down" | "flat" | Direction the number moved since the comparison period. |