Sparkline
A compact trend line rendered as a smooth (Catmull-Rom) SVG path with a subtle gradient fill. When interactive, pointer hover scrubs the line: a binary search over the SVG path length finds the point under the cursor, moving an indicator dot and a thin vertical scrub line while a colored overlay reveals the path up to the cursor. An optional formatValue label tracks the cursor and reports the interpolated value.
Import
import { Sparkline } from "@roger-emerson/ploxum-primitives";Usage
<div className="w-72">
<Sparkline
data={[42, 44, 45, 43, 47, 48, 49, 51, 53, 58]}
formatValue={(n) => `${n.toFixed(1)} GB/s`}
onValueChange={(value, index) => console.log(value, index)}
/>
</div>A static, non-interactive line — the same renderer KpiTile uses inline:
<Sparkline data={trend} height={120} interactive={false} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
data | number[] | — | Y values, evenly spaced along the X axis. |
color | string | var(--ploxum-color-chart-series1) | Line + fill hue. The muted base path uses the tertiary text token. |
height | number | 160 | SVG height in px. The internal viewBox is 400 × height with preserveAspectRatio="none". |
interactive | boolean | true | Enables cursor scrubbing. Falls back to a static line when falsy. |
formatValue | (n: number) => string | — | Maps the scrubbed value to a label that tracks the cursor. Omit to hide the label. |
onValueChange | (value: number, index: number) => void | — | Fired on scrub enter/move with the interpolated value and nearest data index. |
onActiveChange | (active: boolean) => void | — | Fired on scrub enter (true) and leave (false). |
className | string | — | Merged onto the root wrapper. |
ref | Ref<HTMLDivElement> | — | Forwarded to the root wrapper. |
Behavior
- Static fallback. When
interactive={false}— or whendatahas fewer than 2 points — the component renders a plainrole="img"line with no scrub UI. The scrubbing layer is a progressive enhancement, never a hard dependency on pointer input. - Dual-path reveal. A muted base path (tertiary text token at low opacity) is always visible; a colored overlay is clipped via
clip-path: inset(...)to the cursor's X percentage, so the line reads as "colored up to here, muted beyond." - Pixel-accurate scrubbing. The cursor's X is mapped to a point on the curve by a ~25-iteration binary search over
path.getPointAtLength, so the indicator dot sits exactly on the line regardless of curvature.
Reduced motion
When prefers-reduced-motion: reduce is set (via usePrefersReducedMotion), the draw-in animation is suppressed — no @keyframes spark-draw, no scrub-layer transitions. Scrubbing stays fully functional; it simply snaps instantly with no easing.
