BandChart
Time series inside an expected-range envelope with breach emphasis.
Source
"use client"
import { BandChart } from "@/registry/warren/band-chart/band-chart"
/** Deterministic latency samples (ms) with a ±8ms envelope around 24ms. */
const SAMPLES = [
{ t: 0, v: 26 },
{ t: 30, v: 21 },
{ t: 60, v: 35 },
{ t: 90, v: 24 },
{ t: 120, v: 18 },
{ t: 150, v: 33 },
{ t: 180, v: 27 },
{ t: 210, v: 40 },
{ t: 240, v: 23 },
]
const BAND = SAMPLES.map(() => ({ center: 24, spread: 8 }))
export default function BandChartDemo() {
return (
<div className="flex flex-col gap-2 border-2 border-border bg-panel p-2">
<span className="font-mono text-[10px] uppercase tracking-wide text-muted-foreground">
P99 LATENCY · EXPECTED 24±8MS
</span>
<BandChart points={SAMPLES} band={BAND} format={(n) => `${n}`} formatT={(t) => `${t}s`} />
</div>
)
}
Installation
npx shadcn@latest add https://warren.eduard3v.com/r/band-chart.jsonUsage
import { BandChart } from "@/components/ui/band-chart"<BandChart
points={[{ t: 0, v: 4 }, { t: 1, v: 15 }, { t: 2, v: 5 }]}
band={[{ center: 6, spread: 4 }, { center: 6, spread: 4 }, { center: 6, spread: 4 }]}
/>Encoding
The series renders in tone (default primary) inside the expected-range envelope: one flat
translucent tone wash (color-mix at 14% — the sanctioned wash) plus dashed ink hairline
bounds. band accepts either { lower, upper } bounds or { center, spread } envelopes,
aligned by index with points. Points outside their range flip the overlay stroke and a 5px
square marker to breachTone (danger by default) — signal color = state.
Axes are mono tabular numerals on hairline gridlines: format renders y values, formatT
renders t labels. variant="step" switches the series (and envelope bounds) to
horizontal-then-vertical interpolation for discrete-rate metrics. The y domain covers every
value and band edge unless yDomain pins it.
Interaction
Source
"use client"
import { BandChart } from "@/registry/warren/band-chart/band-chart"
/** Discrete rate samples — step interpolation with pinned y domain and success tone. */
const RATES = [
{ t: 0, v: 0.42 },
{ t: 1, v: 0.55 },
{ t: 2, v: 0.51 },
{ t: 3, v: 0.68 },
{ t: 4, v: 0.6 },
{ t: 5, v: 0.47 },
]
const BAND = RATES.map(() => ({ lower: 0.45, upper: 0.65 }))
export default function BandChartBreaches() {
return (
<div className="flex flex-col gap-2 border-2 border-border bg-panel p-2">
<span className="font-mono text-[10px] uppercase tracking-wide text-muted-foreground">
THROUGHPUT RATE · SLO BAND 0.45–0.65
</span>
<BandChart
points={RATES}
band={BAND}
variant="step"
tone="success"
yDomain={[0, 1]}
format={(n) => n.toFixed(2)}
formatT={(t) => `m${t}`}
/>
</div>
)
}
With onPointClick, each point gains a full-column transparent hit rect and the container
flips from role="img" to role="group".
API Reference
Prop
Type
Prop
Type
Prop
Type