WaterfallChart

WaterfallChart draws a running total: an opening anchor, one bar per step, and a closing anchor. Bars are plain divs, so Tailwind classes style them. The chart has no unit. The parent builds the steps, formats each value, and supplies the tooltip content.

import WaterfallChart from "@haloid/azucar/ui/waterfall-chart";
import type { WaterfallStep } from "@haloid/azucar/ui/waterfall-chart/types";

Steps

Each step extends WaterfallStep:

  • key: a stable React key.
  • label and shortLabel: shortLabel shows on mobile.
  • sublabel: the line under the label.
  • from and to: the running total before and after the step.
  • anchor: true for a total. An anchor has from === to and draws from the axis.

Extra fields pass through to valueFormatter and getTooltipContent. Keep domain data on the step. The chart does not read it.

Sizing

The root fills its parent's width. Its height is h-80 md:h-120. All geometry derives from the measured size. buffer is the horizontal padding in px. The default is 48, or 24 on mobile. The demo sets it to 0 because the card has its own inset.

The demo shows three steps between an opening and a closing total.

<div className="w-full">
<WaterfallChart
  steps={[
    { key: "open", label: "Opening", shortLabel: "Open", sublabel: "Subtitle", from: 1200, to: 1200, anchor: true },
    { key: "a", label: "Step A", shortLabel: "A", sublabel: "Subtitle A", from: 1200, to: 1060, anchor: false },
    { key: "b", label: "Step B", shortLabel: "B", sublabel: "Subtitle B", from: 1060, to: 1320, anchor: false },
    { key: "c", label: "Step C", shortLabel: "C", sublabel: "Subtitle C", from: 1320, to: 900, anchor: false },
    { key: "close", label: "Closing", shortLabel: "Close", sublabel: "Subtitle", from: 900, to: 900, anchor: true },
  ]}
  valueFormatter={(step) => {
    if (step.anchor) return String(step.to);
    const delta = step.to - step.from;
    return `${delta > 0 ? "+" : "−"}${Math.abs(delta)}`;
  }}
  getTooltipContent={(step) =>
    step.anchor ? null : {
      title: step.label,
      rows: [{ label: "Change", value: String(step.to - step.from) }],
      description: "What this step accounts for.",
    }
  }
  buffer={0}
  ariaLabel="Waterfall from an opening total through three steps to a closing total"
/>
</div>

Values

valueFormatter(step, compact) returns the text above a bar. compact is true on mobile. Use it to return a shorter form.

Tooltip

getTooltipContent(step) returns Tooltip content, or null. Steps with content get a hit area. Return null for anchors. The tooltip follows the pointer and stays inside the chart.

Bars

barClassName(step) returns the Tailwind classes for a bar. The default paints anchors with surface-aqua and deltas with surface-aqua aqua-gray, both with [--aqua-scale:3]. radius is the corner radius in px. The default is 28. Dashed fg-1 connectors join consecutive steps.

Domain

The y domain fits tightly around the running totals. Deltas are often much smaller than the anchors, and a zero-based axis would hide them. Pass domain to override it.

Use in north

PowerWaterfallVertical in north wires this chart. It builds the steps from a PowerReconInput, formats values in kW, and supplies each mismatch class's label and definition as tooltip content. The chart owns geometry and placement. North owns the data and the wording.