# WaterfallChart

How a total got from one figure to another.

- Group: Charts
- Import: `import { WaterfallChart } from '@misoto22/folio/charts'`
- Page: https://ui.misoto22.com/components/waterfall-chart/
- Related: bar-chart, funnel-chart

## When to reach for it

“Why did this change”, where the contributions can be negative. A pie cannot hold a negative slice; a BarChart is right when the parts need not add up to the gap between two totals.

## Anatomy

- **Figure frame** (required) — ChartFigure’s <figure>, with an empty state at zero steps. description is where the ordering caveat goes, and it is sr-only until showTitle is set.
- **Steps** (required) — <WaterfallChart.Bars>: one floating range bar per step, running from the previous total to the new one, with a custom shape over it. Increases and totals take the solid series fill, decreases the 45 degree hatch, so direction survives greyscale and forced colours.
- **Connectors** — connectors, on by default, joining each bar’s closing edge to where the next one starts. Without them a waterfall is a row of bars floating at unrelated heights and the reader has to reconstruct the cascade.
- **Zero baseline** (required) — A reference line at zero that <WaterfallChart.Bars> draws itself. It is what the total bars stand on, and a waterfall with no visible zero asks the reader to take every floating bar on trust.
- **Step labels** — showValues, and it is OFF by default. It prints each step’s signed change beside its bar — worth more here than anywhere else, because an intermediate bar has no baseline under it and its length is the one thing the axis cannot give back.
- **Hidden data table** — The sr-only table carries the CHANGE and the RUNNING TOTAL per step, which is exactly the pair the picture encodes as a length and a position.

## Best practices

### Do

- Turn showValues on. It is off by default and it is the relief for this form’s central weakness: an intermediate bar floats, so a reader can see that a step was small and cannot see how small.
- Leave value off the closing total step. Omitted, it is computed from the deltas above it; typed by hand it can disagree with them, and the chart will draw the disagreement without saying a word.
- Say in description when the step order is editorial, and pass showTitle so the sentence is actually printed. The connectors draw the steps as a sequence and most breakdowns are not one — churn and expansion in the same month are simultaneous — and a reader takes the leftmost bar as the first cause.

### Don’t

- Do not net two opposing movements into one step. A bar reading minus twenty that is really plus one hundred and eighty against minus two hundred is drawn exactly like a quiet month, and showing what moved is the entire purpose of the form.
- Do not compare an intermediate bar with a total bar by eye. Only the totals sit on the zero line; everything between them is a length at an arbitrary height, so a small step high in the cascade and a large one near zero are not on comparable ground.
- Do not reach for a pie when the contributions are signed. This is the form that exists because a pie cannot hold a negative slice — and a BarChart is the right one instead when the parts need not add up to the gap between two totals.

## Accessibility

- The connectors draw the steps as a sequence, and most breakdowns are not sequential — churn and expansion in the same month are simultaneous, and a reader takes the leftmost bar as the first cause. Where the order is arbitrary, say so in description.
- Intermediate bars are floating lengths read against no baseline, so a small step high up the cascade is hard to compare with a large one near zero. Total bars sit on the axis and are the only ones a reader can read absolutely.
- Direction is carried by the label’s sign and the bar’s texture as well as its position, so the reading survives greyscale and forced colours.
- A closing bar with no value is computed from the deltas, which keeps the arithmetic in the data rather than in the caller’s head.

## WaterfallChart

How a total got from one figure to another — an opening balance, the signed contributions that moved it, and where it closed. The form for "why did this change", which a pair of bars cannot answer and a pie chart answers wrongly, because contributions can be NEGATIVE and a slice cannot. Reach for `<BarChart>` when the parts do not have to add up to the gap between two totals, and for `<FunnelChart>` when the quantity only ever shrinks. **The connectors are the claim to be careful about.** They draw the steps as a sequence — this happened, then this — and most breakdowns are not sequential at all: churn and expansion in the same month are simultaneous, and the order they are listed in is an editorial choice. The arithmetic survives any order; the STORY does not, and a reader will take the leftmost bar as the first cause. Two related traps: the intermediate bars are floating lengths read against no baseline, so a small step high up the cascade is hard to compare with a large one near zero; and any step that is itself a net of two larger opposing movements is invisible as such. Where the order is arbitrary, say so in the `description`. Recharts earns its place for the axes, grid and tooltip. Each bar is a floating range — from the running total to the new one — with a custom shape over it, because Recharts has no waterfall mark and the connectors have to be drawn from the same geometry as the bars they join.

### Props

- `config` — `ChartConfig` default `DEFAULT_CONFIG`. The single series — its label and its paint. Only the FIRST entry is read; a waterfall has one quantity and as many bars as it has steps.
- `data` (required) — `WaterfallStep[]`. The steps, in the order they are applied. Order is the arithmetic: the chart does not sort, because a different order is a different total at every intermediate bar.
- `title` (required) — `string`. What the chart shows, in a sentence a reader could act on. Required, and announced to a screen reader even when it is not printed.
- `showTitle` — `boolean`. Prints the title above the plot instead of hiding it from sight.
- `description` — `ReactNode`. A line under the title — the unit, the window, the caveat.
- `children` (required) — `ReactNode`. The composed parts — axes, grid, tooltip, and `<WaterfallChart.Bars>`.
- `className` — `string`. Merged onto the figure, last, so a call site can size or space it.
- `chartProps` — `ComponentProps<typeof RechartsBarChart>`. Escape hatch onto the raw Recharts chart element.
- `formatValue` — `(value: number) => string` default `defaultTick`. Formats every number the chart prints — ticks, labels, tooltip, table.
- `hideDataTable` — `boolean` default `false`. Drops the hidden table view. Only correct when the page prints the data itself.
- `empty` — `ChartEmptyProps | false`. What the chart shows when it has nothing to draw. `false` keeps the axes, for a chart whose emptiness is itself the reading.

## Example — default

```tsx
import { WaterfallChart, formatNumber, type WaterfallStep } from '@misoto22/folio/charts'

<WaterfallChart
  title="ARR bridge, FY24 to FY25"
  showTitle
  description="Thousands of AUD. The steps are simultaneous; the order is editorial."
  data={steps}
  formatValue={formatNumber({ style: 'compact' })}
>
  <WaterfallChart.Grid />
  <WaterfallChart.XAxis />
  <WaterfallChart.YAxis />
  <WaterfallChart.Tooltip />
  <WaterfallChart.Bars showValues />
</WaterfallChart>
```

## Example — subtotals

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { WaterfallChart, formatNumber, type WaterfallStep } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={connectors ? 'joined' : 'loose'}
    onValueChange={(next) => next && setConnectors(next === 'joined')}
    aria-label="Connectors"
  >
    <ToggleGroupItem value="joined">connectors</ToggleGroupItem>
    <ToggleGroupItem value="loose">bars only</ToggleGroupItem>
  </ToggleGroup>

  <WaterfallChart
    title="Operating profit bridge"
    data={steps}
    formatValue={formatNumber({ style: 'compact' })}
  >
    <WaterfallChart.Grid />
    <WaterfallChart.XAxis />
    <WaterfallChart.YAxis />
    <WaterfallChart.Tooltip />
    <WaterfallChart.Bars connectors={connectors} showValues />
  </WaterfallChart>
</div>
```

## Example — against a plan

```tsx
import { WaterfallChart, formatNumber, type WaterfallStep } from '@misoto22/folio/charts'

<WaterfallChart
  title="ARR bridge against plan, H1 to H2"
  showTitle
  description="Thousands of AUD. The steps are simultaneous; the order is editorial."
  data={steps}
  formatValue={formatNumber({ style: 'compact' })}
>
  <WaterfallChart.Grid />
  <WaterfallChart.XAxis />
  <WaterfallChart.YAxis label="ARR" />
  <WaterfallChart.ReferenceLine y={PLAN} label="Plan" weight="firm" />
  <WaterfallChart.Tooltip />
  <WaterfallChart.Bars showValues />
</WaterfallChart>
```
