Bands

/examples/line/line-chart-band.js

A band is a filled ribbon between a lower and an upper value at each point on the X axis. Where an ordinary line answers “what is the number”, a band answers “how sure are we about it”. That makes it the natural shape for confidence intervals, forecast cones, min/max envelopes, and error ranges — anywhere a single line would overstate precision.

In RareCharts a band is just another series in a Line chart. You mark a series with type: 'band' and give each point a lower and an upper instead of a value. The band renders behind the lines, so a central estimate drawn as a normal line sits cleanly on top of its own range.

Data format

A band series carries { date, lower, upper } points. It mixes freely with ordinary line series in the same setData call — order matters only for layering, and bands are always drawn behind lines regardless.

chart.setData([
    {
        name: 'Confidence range',
        type: 'band',
        color: '#00aaff',
        fillOpacity: 0.15,
        values: [
            { date: '2025-01-01', lower: 86,  upper: 114 },
            { date: '2025-02-01', lower: 101, upper: 135 },
            { date: '2025-03-01', lower: 96,  upper: 128 },
        ],
    },
    {
        name: 'Estimate',
        color: '#00aaff',
        values: [
            { date: '2025-01-01', value: 100 },
            { date: '2025-02-01', value: 118 },
            { date: '2025-03-01', value: 112 },
        ],
    },
]);

A series is treated as a band when it has type: 'band', or when its first point contains both lower and upper. Dates are parsed via parseDate (…), so strings, timestamps, and Date objects are all accepted.

How bands behave

A band sits behind the lines but stays part of the readout:

  • drawn behind every line and area, above the grid
  • reported on hover as a range — the crosshair tooltip shows lower – upper at the cursor date (e.g. P10–P90: $1.95M – $3.65M), so you can read the interval without estimating it by eye
  • no crosshair dot — a band has two edges and no single point, so a dot would be ambiguous; markers and end labels skip it for the same reason
  • a series is only reported where its data actually exists, so a forecast band stays silent over the historical region instead of snapping to its first point
  • both lower and upper participate in the Y scale, so the axis always grows to contain the full range

The fill uses the series color at fillOpacity (default 0.16). Because it is a translucent fill of the same hue, a band reads correctly on both light and dark themes without any per-theme tuning.

Forecasts

The most common use is a forecast: actuals up to today, a central path beyond it, and a widening range that makes the growing uncertainty visible instead of pretending the future is a single number.

This chart is assembled from three series plus one annotation:

  • band series for the P10–P90 range, with zero width at the anchor date so it fans out from the last actual
  • a solid line for the actuals (history)
  • a dashed line for the P50 central path, starting at the same anchor so the two lines connect
  • a vertical annotation marking the forecast cut-over
const FCAST = '#00aaff';

new RareCharts.Line('#chart', {
    curve: 'monotone',
    endLabels: false,                 // series end at different x — right-edge labels would mislead
    annotations: [
        { date: '2024-06-01', label: 'Forecast →', color: FCAST, strokeDash: '4,3' },
    ],
}).setData([
    { name: 'P10–P90 range', type: 'band', color: FCAST, fillOpacity: 0.16, values: band },
    { name: 'Actuals',        color: '#00aaff', strokeWidth: 2.5, values: actuals },
    { name: 'Forecast (P50)', color: FCAST, strokeDash: '5,4', values: p50 },
]);

The convention here is the standard one for forecasts: a single hue throughout, the past drawn solid, the future continuing dashed, and the cone shaded in the same color. Anchoring both the band and the P50 line on the last actual point (2024-06-01) keeps the hand-off seamless — no gap, no jump.

A band only needs the two boundaries you already compute. If your model emits quantiles, map the outer pair to lower/upper and pass the median as an ordinary line.

When everything shares one color, the legend has to carry the distinction. Set `type` on each legend entry so its swatch matches what is on screen — a solid rule, a dashed rule, or a filled square:

legend: [
    { label: 'Actuals',        color: '#00aaff' },                   // solid line
    { label: 'Forecast (P50)', color: '#00aaff', type: 'dashed' },   // dashed line
    { label: 'P10–P90 range',  color: '#00aaff', type: 'band' },     // filled square
]

Stacking several bands — for example P10–P90 around an inner P25–P75 — is just two band series with different opacities. Draw the wider, lighter range first so the tighter one reads on top.

Band series options

A band is configured entirely through fields on its series object. It shares the common chart options (title, subtitle, legend, legendPosition, source, theme) documented on the Settings page, and lives inside a standard Line chart, so all of that chart’s axis and layout options apply too.

Field Type Default Description
Band series
type 'band' Marks the series as a band. Optional when each point already has lower and upper.
name string 'Series N' Series name — used to key the rendered band.
color CSS color theme palette Fill color of the ribbon.
fillOpacity number 0.16 Opacity of the fill. Falls back to areaOpacity if set, then to the chart default.
curve string global curve Curve type for the band edges, e.g. 'monotone', 'linear', 'step'.
values array [{ date, lower, upper }, ...] — the lower and upper bound at each point.