Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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

PropTypeDefaultDescription
labelstringTile label (uppercased). Required.
valuenumber | stringThe metric. Required.
unitstringUnit suffix after the value.
deltanumberChange vs. previous period; renders a success/warning badge.
deltaUnitstringSuffix on the delta badge.
trendnumber[]Sparkline points; omit for no sparkline (needs > 1 point).
tone"ok" | "warning" | "critical" | "amber""ok"Left-border tone; warning/critical also show a StatusPip.
animatebooleanfalseCount the value up via AnimatedNumber (numeric values only; reduced-motion snaps to the final value).
format(n: number) => stringMap the tweened value to display text when animate is set.
interactivebooleanfalseRender as a drill-down trigger (button role + at-rest affordance). Implied when onClick is set.
onClick() => voidDrill-down handler; its presence makes the tile interactive.
hoverContentReactNodeWhen 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.
classNamestringMerged onto the root.
refRef<HTMLDivElement>Root ref.

Design rules

  • interactive / onClick is 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 StatusPip so status reads by shape + colour, never hue alone.
  • The trend Sparkline renders static (non-interactive) inside a tile — the tile is the click target, not the sparkline.
  • hoverContent is pointer-only enrichment (it opens on hover via HoverCard); don't bury anything actionable there. Keep the drill-down (onClick) as the keyboard/touch path to the same detail.

Storybook

Open KpiTile in Storybook ↗