accepted

Bands / references / envelopes as LineChart reference layers

LineChart gains `bands` (shaded rectangles), `references` (horizontal / vertical / diagonal lines), and `envelopes` (signed-area fills between two bounds). Unlocks five editorial ideas from the cross-sport lab in one packet.

LineChart chart-apinew-primitiveseditorial

Context

Five distinct editorial chart ideas from the cross-sport lab (required-rate pace, Elo river, aging curve ±σ, rolling-form CI, Lorenz / Gini) all needed the same shared infrastructure:

  • Axis-aligned zone bands (title race, relegation, confidence regions)
  • Declarative horizontal / vertical / diagonal reference lines (manager changes, regression fits, equality diagonals)
  • Signed-area fills between two bounds (actual vs pace, centre ±σ, Lorenz below equality)

Each had been catalogued as a separate 🟡 “small extension” in the roadmap. Building them independently would drift naming, semantics, and warning codes.

Decision

Ship a single chart reference layers packet delivering:

  • Two new plot primitivesChartPlotAreaBands, ChartPlotAreaReferenceLines (with layer="body" | "labels" | "both" for z-order control and Liang-Barsky segment clipping for diagonals).
  • LineChart extensionsbands, references, envelopes props. Envelopes come in three kinds: series-pair (two series as bounds), center-offset (centre series ± per-point upper/lower), and series-to-reference (series vs a declared reference line — the Lorenz single-source-of-truth case).
  • Six helper factoriesmanagerEventRef, seasonEventRef, goalEventRef, diagonalFromLinear, envelopeCenterOffset, diagonalSeries. Typed with Omit<> so callers can’t override computed geometry fields.
  • Breaking migration — delete events prop and LineChartEventInput / LineChartEventModel types. Replaced by references + helpers. Meta field validSeries splits into dataSeries (valid points) and visibleSeries (not hidden).

Consequences

  • Adversarial review loops surfaced 4 P0 findings before ship: band / reference validation needed to live in compute (not primitive) so warnings reach model.meta.warnings; [envelope.truncated] must fire when a source series has dropped points; all-hidden charts must return empty state; diagonal references must clamp to their declared from..to x-support (no silent extrapolation).
  • Primary consumer in apps/site migrated in the same commit (events → references). No deprecation shims — this is pre-release v0.2.
  • Each envelope kind carries its own warning taxonomy ([envelope.unknown-series], [envelope.insufficient-points], [envelope.bounds-mismatch], [envelope.no-overlap], [envelope.vertical-reference], [envelope.inverted-bounds], [envelope.truncated], [envelope.unknown-reference]).
  • XGTimeline’s existing backgroundBands model was NOT migrated — it’s intentionally preserved as a chart-specific concept tied to the compressed time-axis. If demand grows we can merge later.
  • Five cross-sport-lab ideas shipped to 🟢 ships-today status.
← All decisions