Dashboard cluster: equal-height tiles; the first is clickable (tab / click it)
Number is a mono figure, label muted, delta is warm status + a glyph (never color alone), the featured fill uses on-accent, sparkline is brand cyan, all var(--token). Tiles opened 0 · count-up honors reduced-motion. The island is live.
Trend chip: a warm status color + a direction glyph (▲/▼) + a sign. Never color alone. goodWhenDown flips the color for cost/latency (the glyph stays).
icon
ReactNode
n/a
Optional leading glyph, rendered on the ratified neutral hairline plate (muted glyph, transparent fill) by default. Opt into a hue with tint.
spark
number[]
n/a
Optional inline sparkline, normalized to its own min/max with a filled "now" dot. Brand cyan (on-accent when featured).
tone
'default' | 'featured'
'default'
featured fills the tile with the cyan gradient (one per cluster); labels use on-accent.
tint
'accent' | 'success' | 'warning' | 'danger'
n/a
Opt-in icon-plate tint that colors the glyph + plate a soft hue (soft wash + hue border + hue glyph, the Badge-soft recipe). Omit for the settled neutral plate (default renders byte-identical). Ignored on featured tiles.
gradient
boolean
false
EXPERIMENTAL, not ratified canon: a subtle THEME-FOLLOWING surface gradient (a gentle neutral diagonal, not the featured cyan). Default renders the flat surface, byte-identical. Ignored on featured.
size
'comfortable' | 'compact'
'comfortable'
Density. compact is the dense ClusterSheet tile; comfortable the Voice card.
loading
boolean
false
Skeleton shimmer instead of content; holds the tile height and sets aria-busy.
onClick
() => void
n/a
When set, the whole tile becomes one button (hover lift, focus-visible ring, single accessible name).
Parameter
Type
Default
Description
icon *
ImageVector
n/a
Leading glyph (11.dp), tinted by iconTint.
label *
String
n/a
Metric name, rendered in labelSmall on onSurfaceVariant (muted).
value *
String
n/a
Headline figure in FontFamily.Monospace. Format counts with formatTokenCount (1M / 42K).
modifier
Modifier
Modifier
External modifier. Pass Modifier.weight(1f) to tile equal-width across a row.
iconTint
Color?
null
Icon color; falls back to onSurfaceVariant. The cluster sheet uses model-type tints.
trailing
(@Composable -> Unit)?
null
Optional trailing slot: the shipped tile drops a Sparkline here.
NockerlStatCard is the packaged tile, with NockerlCard chrome (surface1 +
catch-light + hairline + the L2 neutral shadow; Voice’s gradient fill retires).
The head band reserves the 40pt plate height even icon-less, so headline numbers
share one offset across a tile row. Not NockerlControlMetrics. That is the
control-layer metric constants (the 44pt touch floor), not a component.
Parameter
Type
Default
Description
label *
String
n/a
Metric name: uppercase, 12pt medium, onCardMuted.
value *
String
n/a
Headline figure, pre-formatted in the Outfit brand family at the display-small size (28pt, light). Pass a dash for the empty state (it mutes), never a misleading 0.
delta
NockerlStatDelta?
nil
(value, trend: .up/.down, goodWhenDown). A warm status color + ▲/▼, never color alone; goodWhenDown flips the color for cost/latency (the glyph stays).
icon
@ViewBuilder
n/a
Leading glyph (SF Symbol Image or any view), muted, inside the ratified neutral hairline plate by default (40pt, radius control, transparent + cardHairline). Opt into a hue with tint.
tint
NockerlStatTint?
nil
Opt-in icon-plate tint. .accent / .success / .warning / .danger color the glyph + plate a soft hue (soft wash 14% + hue border 28% + hue glyph, the Badge-soft recipe). Omit for the settled neutral plate (default renders byte-identical). Warm hues stay status-meaningful; .accent is the earned-cyan case.
density
NockerlControlDensity
.comfortable
The rhythm tier (the shared NockerlControlDensity, one density story, not a bespoke knob): .comfortable is the unchanged 40pt-plate / 28pt-value tile; .compact mirrors the web compact tier with 32pt plate, space3 padding, space2 gap, a 20pt value (≈88→ a ~26% shorter tile) for dense dashboards and small windows. Legibility + the neutral-plate anatomy hold.
Stat card vs. chart vs. context-gauge, and cross-platform drift
Pick the right component.Stat card is the single-KPI tile (one number + label + optional delta/sparkline). For a full plot (line/area/bar, multi-series, axes, a usage donut) use Chart / sparkline. For the live token-budget meter use Context gauge. The tile borrows the chart’s sparkline + formatTokenCount vocabulary, but it is not a chart.
Delta / trend leads on web. The trend chip is designed here from the laws (warm status color + a direction glyph, never color alone) and is the intended @dizyx/nockerl-react API. The Kotlin and Swift tabs document tiles built without it, so the delta is the clearest place the platforms have yet to meet.
Same idea, two builds. Voice StatCard = an accent icon chip + big bold mono number + muted label (the comfortable tile here). Android StatTile = a compact mono tile (small inline icon + label, value + trailing sparkline). The web tile unifies both as size.
Radius drift. Voice hardcodes 10pt, Android 6dp; the tile here uses radius.card (16). The native tiles should consume radius.card.
Count format. Android formatTokenCount is integer (1M / 42K); Voice’s header uses one decimal (8.4k). One compact-number policy needs to win.