Skip to content

Forecast Band

Show a running total’s forecast range and when it may cross a budget.

import { ForecastBand, ForecastPoint, ForecastScenario } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a forecast band for a total that only grows through a period (cloud spend, ad spend, API calls against a quota) when the question is where it lands and when it crosses the line. Spend so far is a solid ink line over a faint wash. From today the forecast carries on as a dashed line in its tone, inside its likely range, over a hatched projected area: solid for what's settled, hatched for what isn't, as in the Hatched Bar. The budget is a dashed line. If the forecast crosses it, the stretch above the line is hatched in danger and a marker lands on the crossing day, with its flag over the plot. For a budget read as what's left, use an Limit Meter; for values over time with axes and tooltips, a Chart.

import { ForecastBand } from '@youj/react'

<ForecastBand
  // The running total at the end of each elapsed day; the last one is today.
  actual={[412, 880, 1335 /* … */]}
  // The forecast running total for each remaining day, with its likely range.
  forecast={[{ value: 8500, low: 8370, high: 8710 } /* … */]}
  budget={12000}
  start="2026-09-01"
  format={{ style: 'currency', currency: 'USD' }}
/>

The period runs from start for as many days as actual and forecast hold between them. Its days are named in the locale ("Sep 28"). Without start, the crossing is named from today ("in 9 days") and the axis numbers the days. The scale tops out at a round number above the budget and the forecast's high. Pass max to hold it still when the data changes, so the axis stays put while the paths morph. For a unit Intl has none for (tickets, kWh), pass formatValue instead of format: a function that writes every amount and the axis labels.

Reference

Propsactual, forecast, budget… (11)

Props

ForecastBand props
PropType
actualRequiredreadonly number[]

The running total at the end of each elapsed day, the last one being today.

–
forecastRequiredreadonly ForecastPoint[]

The forecast running total for each remaining day of the period.

–
budgetRequirednumber–
startDate | string

The period's first day, to name dates ("Sep 28"). Without it the crossing is named relative to today ("in 9 days") and days are numbered.

–
maxnumber

The top of the scale. Default: a round number above the budget and the forecast's high; pass one to keep the scale still across data.

–
formatIntl.NumberFormatOptions

Intl.NumberFormat options for the amounts, e.g. { style: 'currency', currency: 'USD' }.

–
formatValue(value: number) => string

Writes every amount (and the scale) with your own function, for units Intl has none for ("1,240 kWh"). Replaces format.

–
localestring

Default: the YoujProvider locale.

–
labelstring

Names the chart for assistive tech. Default: the labels' "Spend forecast".

–
toneForecastTone
'accent''neutral''chart-1''chart-2''chart-3''chart-4''chart-5'

The forecast's color: its line, range and hatch.

default 'chart-5'

'chart-5'
legendboolean

Show the legend over the plot.

default true

true
scenarioForecastScenario

A second path from today: what the total does if something changes.

–
Other props pass through: extends ComponentProps<'div'>.
StylingParts and data attributes

Styling

Parts: forecast-band, forecast-band-legend, forecast-band-swatch, forecast-band-chart (the image), forecast-band-axis (with data-axis x or y), forecast-band-tick, forecast-band-plot, forecast-band-data, forecast-band-grid, forecast-band-art (the drawing, mirrored in RTL), forecast-band-actual, forecast-band-actual-area, forecast-band-actual-line, forecast-band-forecast, forecast-band-range, forecast-band-forecast-line, forecast-band-projection, forecast-band-overshoot, forecast-band-today, forecast-band-today-label, forecast-band-now, forecast-band-budget, forecast-band-flag (the budget's and the crossing's), forecast-band-flag-name, forecast-band-flag-value, forecast-band-crossing (with data-day), forecast-band-marker, forecast-band-pulse, forecast-band-leader, forecast-band-scenario-line, forecast-band-scenario-end and forecast-band-summary. The root has data-state (over or within). tone sets the forecast's color, a chart color by default, as --forecast-tone, and the scenario's tone is --forecast-scenario-tone. --forecast-band-height sets the plot's height (13rem). The flags and the today chip take the theme's label voice.

MotionReveal: actual, reveal: forecast, crossing… · reduced motion

Motion

Motion tokens
MomentTokens
Reveal: actualwhen a third of the chart scrolls into view, the spend line and its wash draw in from the start to today (a clip, so nothing stretches)
duration.deliberateease.emphasized

560ms · cubic-bezier(0.6, 0, 0.1, 1)

560mscubic-bezier(0.6, 0, 0.1, 1)
Reveal: forecastthe forecast, its range and the hatch wipe on from today to the period’s end, starting as the actual line lands
duration.slowease.enter

340ms · cubic-bezier(0.16, 1, 0.3, 1)

340mscubic-bezier(0.16, 1, 0.3, 1)
Crossingthe marker arrives as the wipe reaches it, and its ring pulses once; the flag fades in over it
spring.settle

0.45s, bounce 0.3

0.45s, bounce 0.3
New dataevery path morphs to the new values (keep the point counts stable), and the budget line and the marker glide; the ring pulses once more only if the crossing day moves
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
No crossingthe marker and its flag leave
duration.fastease.exit

140ms · cubic-bezier(0.4, 0, 1, 1)

140mscubic-bezier(0.4, 0, 1, 1)

Reduced motion

Drawn in place: no draw or wipe, the marker's ring static, new data landing at once.

Reduced motion

Everything is drawn in place. There's no draw, no wipe and no pulse, and new data lands at once.

AccessibilityLabels and assistive technology

Accessibility

  • The plot is an img named by label (by default the labels' "Spend forecast"). It's described by a text summary: what's spent of the budget, the forecast at the period's end and its likely range, and the crossing day ("Crosses budget Sep 28") or "Stays within budget".
  • The legend over the plot is a list of text, so it reads as it looks. The lines, the hatch, the marker and the axes are inside the image.
  • Time runs from the inline start, like the kit's other rulers, so the plot mirrors in RTL. It holds no text; the flags and axis labels stay upright and in their own order.
  • Its built-in text comes from the labels, so <YoujProvider labels> translates it.