registry:ui · General
statistic — the metric readout
Micro-label over a big tabular-nums value with prefix/suffix snippets and text-glyph trends. The component never guesses what good means for your metric — you compose it.
$npx jixoai-ui add statistic
https://ui.jixoai.com/r/statistic.json
zero-dependency; recipes add press-button + badge
usage
Usage
The value is yours — strings or numbers render verbatim; formatting happens before the component.
<Statistic title="deploys / week" value="42" trend="up" />examples
Examples
Ability-named recipes: the countdown and the affix + precision matrix.
Both recipes compose the public value/snippet surface — the component renders what it is handed, the tick and the digits are page law (a first-class countdown item is the recorded followup).
with countdown
A 90-second deploy window ticking in mm:ss — start it, pause it, reset it. The tabular-nums face keeps every frame the same width, so the digits flip in place without a single layout shift.
deploy window closes in
01:30
idle
<script lang="ts">
// the countdown, composed: the component renders the value; the tick
// law is page state (a first-class countdown item is the recorded
// followup).
let durationMs = $state(90_000);
let remainingMs = $state(durationMs);
let running = $state(false);
let finished = $state(false);
let timer;
function start() {
if (running) return;
running = true;
timer = setInterval(() => {
remainingMs = Math.max(0, remainingMs - 1_000);
if (remainingMs === 0) { stop(); finished = true; }
}, 1_000);
}
function stop() {
running = false;
clearInterval(timer);
}
const mmss = $derived(
String(Math.floor(remainingMs / 60_000)).padStart(2, '0')
+ ':' + String(Math.floor((remainingMs % 60_000) / 1_000)).padStart(2, '0'));
</script>
<div class="flex items-end gap-4">
<Statistic title="deploy window closes in" value={mmss} />
{#if finished}<Badge variant="fill">window closed</Badge>{/if}
</div>
<div class="flex gap-2">
<PressButton variant="outline" onclick={start} disabled={running || finished}>start</PressButton>
<PressButton variant="ghost" onclick={stop} disabled={!running}>pause</PressButton>
<PressButton variant="ghost" onclick={reset}>reset</PressButton>
</div>
<p class="sr-only" aria-live="polite">{finished ? 'window closed' : ''}</p>with prefix, suffix and precision
One raw number (1234.5) through the affix + precision matrix: the segmented control re-formats the digits live (Intl.NumberFormat), the prefix/suffix snippets carry the glyphs.
revenue / mo
$ 1,234.50 USD
cache hit rate
97.4 %
p95 latency
1,234.50 ms
<script lang="ts">
// formatting is the caller's law: Intl.NumberFormat decides the digits,
// the prefix/suffix snippets decide the glyphs.
const raw = 1234.5;
let digits = $state(2);
const precise = $derived(new Intl.NumberFormat('en-US',
{ minimumFractionDigits: digits, maximumFractionDigits: digits }).format(raw));
</script>
<!-- prefix/suffix are SNIPPETS: any glyph, any paint -->
<Statistic title="revenue / mo" value={precise}>
{#snippet prefix()}<span class="text-muted-foreground">$</span>{/snippet}
{#snippet suffix()}<span class="text-muted-foreground">USD</span>{/snippet}
</Statistic>
<Statistic title="cache hit rate" value="97.4">
{#snippet suffix()}<span class="text-muted-foreground">%</span>{/snippet}
</Statistic>types
Metric states
The readout supports neutral metrics, directional trends and composed affixes.
statistic · states
neutral
69
up
42 ▲
down
3 ▼
<script lang="ts">
import Statistic from '@ui/statistic.svelte';
</script>
<div class="grid gap-4 sm:grid-cols-3">
<Statistic title="neutral" value="69" />
<Statistic title="up" value="42" trend="up" />
<Statistic title="down" value="3" trend="down" />
</div>a11y
Accessibility
ARIA
| Attribute | Value | Description |
|---|---|---|
| title | visible label | Names the metric for every reader. |
| value | text content | Keeps formatted values readable and copyable. |
| aria-live (recipe) | polite, one-shot | The countdown announces the CLOSE, never every tick — a ticking live region is noise. |
theming
Density and tokens
deploys
42 ▲
deploys
42 ▲
deploys
42 ▲
| Token | Default | Source |
|---|---|---|
| --jx-stack | density scale | density |
| --jx-gap | density scale | density |
| --jx-text | density scale | density |
| --jx-text-secondary | density scale | density |
| --jx-line | density scale | density |
axes
Universal props
The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive/container-conditional values. The metric readout is flat content, all no-own; the size axis scales the root and the supply chain is the point. Radius is STAMPED, NEVER CONSUMED (measured, task 111): the readout is flat text with no corner geometry to round — the family reads no --jx-radius-* var (the color-picker #19 class); the demo lane teaches the stamp, the paint stays honest about it.
Statistic · universal props
axes joined
42
named steps
97.4%
<Statistic title="axes joined" value="42" size={18} density="small" />api
Statistic props
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| title * | string | — | Metric label. |
| value * | string | number | — | Displayed metric value (format before it reaches the component). |
| trend | 'up' | 'down' | — | Optional directional glyph. |
| prefix | Snippet | — | Content before the value. |
| suffix | Snippet | — | Content after the value. |
| class | string | '' | Forwarded to the readout root; consumer classes land last. |
Universal props
| Property | Type | Default | Description |
|---|---|---|---|
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | the base scale — root font-size; parts size in em |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' · ambient scope | corner geometry (CSS corner-shape; §14 degrade table) |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | corner size; auto = the concentric broadcast (§3) |
| density | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | spacing/leading scale over the kernel channels (§4) |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' · ambient scope | the hue axis of the fixed oklch primary system (§5) |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' · ambient scope | light/dark profile; system = the JS-mutable global (§6) |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' · ambient scope | official M3 levels over the surface ladder (§7) |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' · ambient scope | intensity across the motion kernels (§8) |
see also
See also
The readouts and surfaces statistic composes with.