# Explainable total

A React component in [Beamline](https://beamline.io/)'s [Dashboard](https://beamline.io/components/dashboard) category. Live demo: https://beamline.io/components/explainable-total

A total that answers "why this much?" in place: it unfolds into the steps that made it — a waterfall whose bars start where the last step ended, each step with its kind, its reason in real numbers, its source and a thread to the steps it came from; where the host allows, a step's lever (add-on, discount, quantity) re-solves the rest, a pinned version shows what changed, and a re-solved total waits to be applied.

## Use it for

- A number with money in it that people question: a quote, an invoice, a refund, a checkout, a commission, a first month's rent.
- The host computes every amount and writes each step's reason; the part presents, threads and animates and never adds money up itself.

## Not for

- A KPI with a trend → [metric-card](https://beamline.io/components/metric-card).
- A table of many invoices → [living-table](https://beamline.io/components/living-table) or [data-grid](https://beamline.io/components/data-grid).

## Anatomy

- rest: the label, the total (or what's due now of it), Estimate / Not applied when true, Why?
- sheet: the label and Close; the steps; the total row with its bar, the due-now mark and, when pinned, the difference; pin and apply
- step: glyph and kind word, label (lock, set-by chip), a waterfall bar on one shared scale (a soft end for a range, an outline for the pinned version), the amount with its difference; the lever; the reason with its source while read; the host's next-break nudge; parts under it

## States

- rest
- open
- pending (the last total readable, dotted, Recalculating…)
- error (no number, Try again)
- unapplied (Apply $…)
- pinned (outlines and differences)
- ineligible step (struck, not counted, with why)
- locked step
- range

## Keyboard

- Enter or Space on the total opens it; Escape or Close folds it back with focus on the total
- Tab reaches each step's label (its reason shows and its basis lights) and each lever: switch, slider (arrows), number field (arrows, typing)
- A lever's change announces the new total and the difference

## Motion

The total unfolds in place (the shared height morph). A lever's change moves the bars to their new places and rolls the changed digits (slot text); the thread draws between a step and the steps it came from. Reduced motion places at once.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | What the total is: "Quote total", "Refund", "First month". |
| `steps` (required) | `TotalStep[]` | — | Drawn in this order, each bar starting where the last ended; the host recomputes every amount when a lever moves (a lever may change any step, not only its own). |
| `total` (required) | `TotalAmount` | — | The host's total; the part never adds money up itself. |
| `due` | `number` | — | What is due now, when it isn't all of it (a deposit). |
| `estimate` | `boolean` | — | An estimate says so at rest and in the sheet. |
| `currency` | `string` | — |  |
| `format` | `((amount: number) => string)` | — | Money in the host's own format; default: the locale's currency format for `currency`. |
| `status` | `"error" \| "pending" \| "ready"` | — | "pending" while the host recalculates (the last total stays readable), "error" when it can't. |
| `onRetry` | `(() => void)` | — |  |
| `onLeverChange` | `((stepId: string, value: number \| boolean) => void)` | — |  |
| `applied` | `number` | — | The amount actually applied, when a re-solved total is only a suggestion until applied. |
| `onApply` | `(() => void \| Promise<unknown>)` | — |  |
| `pinnable` | `boolean` | — | Offer "Pin this version" to compare against. Default true when any step has a lever. |
| `open` | `boolean` | — | Controlled open state; omit to use defaultOpen. |
| `defaultOpen` | `boolean` | — | Initial open state when uncontrolled; defaults to false. |
| `onOpenChange` | `((open: boolean) => void)` | — | Reports an open-state change; no callback by default. |
| `className` | `string` | — | Classes on the documented visual root; no override by default. |
| `classNames` | `Partial<Record<"root" \| "row" \| "total" \| "trigger" \| "actions" \| "bar" \| "sheet", stri…` | — | Classes for the named visual parts; no overrides by default. |
| `style` | `CSSProperties` | — | Inline styles on the documented visual root; no overrides by default. |
| `ref` | `Ref<HTMLDivElement>` | — | Receives the documented native target or imperative handle; omitted by default. |
| `onLeverCommit` | `((stepId: string, value: number \| boolean) => void)` | — | A lever's settled value: a slider released, a switch flipped, a quantity entered. Recalculate on the server here; `onLeverChange` follows every step for an instant local answer. |

## Built from

- [Button](https://beamline.io/components/button.md)
- [Label](https://beamline.io/components/label.md)
- [Number field](https://beamline.io/components/number-field.md)
- [Slider](https://beamline.io/components/slider.md)
- [Slot text](https://beamline.io/components/slot-text.md)
- [Spinner](https://beamline.io/components/spinner.md)
- [Switch](https://beamline.io/components/switch.md)
- [Tooltip](https://beamline.io/components/tooltip.md)

## Used in

- [Payroll run](https://beamline.io/components/payroll-run.md)

## Dependencies

`lucide-react`

## Import

```tsx
import { ExplainableTotal } from "@beamline/explainable-total";
```

## Get it

Explainable total is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in Dashboard

- [Explainable change](https://beamline.io/components/explainable-change): A metric whose change explains itself: at rest the value, the change against a named period and a thin ribbon of its causes under the change; opened in…
- [Budget vessels](https://beamline.io/components/budget-vessels): Budgets as glasses of money: the liquid is what is left (free money on top, committed money as a lighter layer under it), the lip at the period's budget…
- [Insight cards](https://beamline.io/components/insight-cards): Findings from an agent, one per page: the sentence, the series it is about with their change, a chart to scrub that names each point, and a follow-up…
- [Tracker](https://beamline.io/components/tracker): A row of status blocks for uptime, job runs or daily checks, with tooltips and a legend.
- [Metric card](https://beamline.io/components/metric-card): A KPI that fills its tile: label, a value that rolls to each new number, the change with arrow, sign and tone, what it compares with and the value it…
- [Category bar](https://beamline.io/components/category-bar): One bar split into labelled ranges with an optional marker that names its range.
- [Bar list](https://beamline.io/components/bar-list): Ranked horizontal bars, label and value over a meter, one line per row when there is room — top pages, sources, customers.
- [App shell](https://beamline.io/components/app-shell): The frame for every app screen: collapsible sidebar with grouped navigation, top bar and page.

Part of [Beamline UI: polished, editable React components and complete screens for custom applications](https://beamline.io/). [Build your own style from any look; tokens, fonts, radii, motion; not only colours](https://beamline.io/docs/customize).
