KpiTile
A compact metric tile — label, big value (optionally count-up animated), a delta badge, and an optional Sparkline trend. Tiles can be made drillable so clicking opens a detail view.
Import
import { KpiTile } from "@roger-emerson/ploxum-primitives";Usage
<KpiTile
delta={0.4}
deltaUnit="%"
label="Availability"
tone="ok"
trend={[42, 44, 45, 46, 47, 48, 49, 48, 50, 51]}
unit="%"
value={91}
/>Interactive (drill-down)
Set interactive (or pass onClick) to turn a tile into a drill-down trigger: it gains a button role, keyboard activation (Enter / Space), and an at-rest affordance — an amber ring + soft underglow + a corner "expand" glyph — that brightens and lifts 1px on hover (reduced-motion gated).
<KpiTile
interactive
label="Open incidents"
onClick={() => openIncidentBreakdown()}
tone="warning"
value={4}
/>Hover preview
Pass hoverContent to attach a HoverCard preview that reveals richer context (breakdowns, last-updated, who's assigned) on pointer dwell — without leaving the tile or committing to the drill-down click. On a non-interactive tile this also auto-adds a lighter affordance — an amber border-highlight on hover plus a small info hint glyph — so the tile signals it has more to reveal. When the tile is interactive, the drill-down expand glyph takes precedence instead.
<KpiTile
hoverContent={
<div className="text-[12px]">
<div className="font-medium">Open incidents · by gate</div>
<div className="text-muted-foreground">Gate 4 · 2 — Gate 1 · 1 — VIP · 1</div>
</div>
}
interactive
label="Open incidents"
onClick={() => openIncidentBreakdown()}
tone="warning"
value={4}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Tile label (uppercased). Required. |
value | number | string | — | The metric. Required. |
unit | string | — | Unit suffix after the value. |
delta | number | — | Change vs. previous period; renders a success/warning badge. |
deltaUnit | string | — | Suffix on the delta badge. |
trend | number[] | — | Sparkline points; omit for no sparkline (needs > 1 point). |
tone | "ok" | "warning" | "critical" | "amber" | "ok" | Left-border tone; warning/critical also show a StatusPip. |
animate | boolean | false | Count the value up via AnimatedNumber (numeric values only; reduced-motion snaps to the final value). |
format | (n: number) => string | — | Map the tweened value to display text when animate is set. |
interactive | boolean | false | Render as a drill-down trigger (button role + at-rest affordance). Implied when onClick is set. |
onClick | () => void | — | Drill-down handler; its presence makes the tile interactive. |
hoverContent | ReactNode | — | When set, wraps the tile in a HoverCard whose panel shows this content on pointer dwell — a pointer-only preview, not a replacement for the drill-down. On a non-interactive tile it also adds an amber hover border-highlight + info hint glyph. |
className | string | — | Merged onto the root. |
ref | Ref<HTMLDivElement> | — | Root ref. |
Design rules
interactive/onClickis for tiles that drill into a detail view (a drawer, a route) — the corner glyph reads as "expand". Don't make a tile interactive without a real destination.- Warning/critical tones carry a
StatusPipso status reads by shape + colour, never hue alone. - The trend
Sparklinerenders static (non-interactive) inside a tile — the tile is the click target, not the sparkline. hoverContentis pointer-only enrichment (it opens on hover viaHoverCard); don't bury anything actionable there. Keep the drill-down (onClick) as the keyboard/touch path to the same detail.
