GradedBand
React graded band chart — a measured value seated inside qualitative grade bands. Tiny accessible SVG microchart; RSC-safe static or /interactive.
GradedBand answers "how sure are we about this one number?". Nested central intervals are graded by opacity, with a median tick. It is never a bar from zero — a bar with an error bar induces edge-literalism bias, so this form exists precisely to avoid it. Opacity maps to probability level and nothing else.
Install
import { GradedBand } from "@microcharts/react/graded-band";// posterior — real values under “Sample data” below<GradedBand data={posterior} label="median" title="Forecast estimate" />Sample data
// posterior draws for one estimate (deterministic pseudo-sample)const posterior = Array.from( { length: 160 }, (_, i) => 21 + Math.round(9 * Math.sin(i) + 6 * Math.sin(i * 2.3)),);Try it
When to use it
- Good for — a forecast with its uncertainty, estimate-vs-actual in a KPI card, posterior summaries.
- Avoid for — countable odds (QuantileDots) or a forecast over time (ForecastCone).
Sizing
Variants
With a locale, the median label and the accessible summary follow that locale's decimal mark — the label above renders
"2,2", not "2.2".
Edge cases
The accessible name for that strip is "Point value 42, no interval." — a degenerate sample is announced as a point estimate, never dressed up as certainty about a range.
Four homes
Why this default
Nested central intervals graded by opacity — never a bar from zero. A bar with an error whisker invites reading the bar's end as "the answer" and the whisker as noise; the graded band has no endpoint to over-read, so opacity maps to probability and nothing else. Inner intervals are clipped inside their outer, so quantile rounding can never invert the nesting.
Accessibility
The accessible name states the median and the innermost + outermost intervals — for the forecast above that's "Median 22; 50% within 15–27, 95% within 8–36." The interactive entry roves the band edges and announces each interval's coverage and bounds.
The interactive entry follows the shared interaction contract:
arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or
Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape.
On touch, a tap pins and a drag scrubs.
Props
| Prop | Type | Notes |
|---|---|---|
| data* | number[] | Sample / posterior draws; the component derives the intervals. |
| levels | number[] | 1–3 nested central intervals (default [50, 80, 95]). |
| value | number | Observed value overlaid as a dot. |
| softEdge | boolean | Fade past the outer band — 'this is approximate'. |
| label | "median" | "none" | States the median in a right gutter (default none). |
| animate | boolean | interactiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion. |
Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.