SearchCtrlK

  • ↑↓navigate
  • ↵open
  • escclose

Can’t find it? Contact us

StatCard

Stat cards for a dashboard.

Uptime
99.98%
Up+0.02%vs last week
One 4-minute outage on Tuesday
p95 latency
182 ms
Up+12 msvs last week
p95 latency, rising over the last 12 hours
Slowest in eu-west since the 14:02 deploy
Error rate
0.4%
Down−0.3%vs last week
Mostly 502s from the billing API
Open incidents
2
No change0vs yesterday
Both on checkout, oldest 3h 12m
View code
tsx
<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.

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.

Terminal
npm add @sdxc/ui remix
typescript
import "@sdxc/ui/theme.css";

Usage

typescript
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.

  • StatCard
  • StatCard.GroupLays stats out by the width of their container: one column, two compact ones from 18rem, two full-size from 28rem, and columns from 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-size across 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

Checks run
1.21M
+4.1%from last week
Monitors down
1
−2from last week
Mean response
143 ms
−9 msfrom last week
Alerts sent
38
+11from last week
View code
tsx
<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

1h ago: 42k requests3h ago: 38k requests5h ago: 41k requests7h ago: 56k requests9h ago: 64k requests11h ago: 60k requests
View code
tsx
<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
tsx
<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.

PropTypeRequiredDescription
hrefstringNoMakes the whole card a link, such as the metric's chart tab or its detail page.
currentbooleanNoMarks a link card as the metric the page shows, through aria-current="true".
childrenRemixNodeYes

Also accepts everything in Omit<TagProps<"div">, "children">.

StatCard.Group

PropTypeRequiredDescription
variant"cards" | "ruled" | "tabs"No
columnsnumberNoColumns 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

PropTypeRequiredDescription
trend"up" | "down" | "flat"Yes
invertbooleanNoTones a fall as good and a rise as bad, for numbers such as latency or error rate.
trendLabelstringNoSpoken before the change, since the arrow is decorative ("Increased by").
comparisonRemixNodeNoThe period it compares with, set after the pill ("vs last week").

Also accepts everything in TagProps<"span">.

StatCard.Chart

StatCard.Skeleton

PropTypeRequiredDescription
deltabooleanNoReserves a delta pill.
chartbooleanNoReserves the trend slot.
footerbooleanNoReserves one footer line.

Also accepts everything in Omit<TagProps<"div">, "children">.

Types

NameValuesWhat 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.
@sdxc/ui

Can’t find it? Contact us