# Bridge Charts

> Waterfall and bridge charts in depth - ARR, revenue, EBITDA, cash, and headcount bridges; anchors, deltas and subtotals; multi-level and stacked bridges; connector lines, sign encoding, label placement, and the footing rule that a bridge which does not sum is a wrong analysis rather than a chart to adjust. Trigger on "waterfall", "bridge", "walk", "how did we get from", "ARR bridge", "cash bridge", "EBITDA bridge", "explain the change", "variance walk", "roll forward chart".

- Skill: `lukehle/bridge-charts` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lukehle/bridge-charts`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lukehle/bridge-charts/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: Lukehle (https://skillmd.com/u/lukehle)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lukehle/bridge-charts

---


# Bridge charts

The most valuable chart in finance and the most consistently under-used. A bridge answers *how we got
from A to B* — the one question a table of two numbers cannot.

Build it with `chartkit.waterfallLayout` (see `svg-charting`), which computes the geometry and, more
importantly, checks that the bridge foots.

---

## The rule that comes before any styling

> **The bars must sum exactly to the closing anchor.**

If they do not, the decomposition is wrong. Fix the analysis. **Never add a plug, never quietly widen
"other", never adjust the closing anchor to match.** A bridge that foots because of a plug is a chart
that asserts a false explanation.

```js
const {bars, domain, footing} = chartkit.waterfallLayout(items);
if (footing && !footing.ok) {
  throw new Error(`Bridge does not foot by ${footing.variance} — fix the analysis`);
}
```

Fail loudly. A silent plug is the single worst failure mode in financial charting because it is
invisible in the output.

---

## Anatomy

| Bar type | Sits | Encodes |
|---|---|---|
| **Anchor** | On the baseline | An absolute state — opening, closing |
| **Delta** | Floating, from the running total | A change |
| **Subtotal** | On the baseline | The running total at a stage |

```
        ┌───┐                                 ┌───┐
        │   │  ┌──┐                           │   │
 ┌───┐  │   │  │  │  ┌──┐                     │   │
 │   │──┘   └──┘  └──┘  └──┐   ┌──┐           │   │
 │   │                     └───┘  └───────────│   │
 │   │                                        │   │
 └───┘                                        └───┘
Opening   New   Expan  Contr  Churn          Closing
```

Anchors in a neutral colour. Deltas coloured by direction. The connector lines are not optional —
without them the eye cannot follow the cumulative path, and the chart degrades into floating bars.

---

## The four bridges worth knowing

### ARR bridge

```
Opening ARR → New → Expansion → Contraction → Churn → Closing ARR
```

The definitive SaaS growth visual. Rules: a customer appears in exactly one bucket per period;
downgrade-to-zero is churn, not contraction; group New and Expansion visually to make gross-add
versus gross-loss readable at a glance.

### EBITDA / margin bridge

```
Prior EBITDA → Volume → Price → Mix → COGS → Personnel → Other opex → Current EBITDA
```

Order matters here: put the **operational drivers first and the one-offs last**, so the reader sees
the underlying movement before the noise. Add a note reconciling to a clean run rate — that is what
gets used for forecasting.

### Cash bridge

```
Opening cash → Collections → Payroll → Vendors → Capex → Financing → Closing cash
```

**Outflows always negative.** A cash chart with positive outflows will be misread by someone in a
board meeting. Consider a subtotal after operating items to separate operating from financing.

### Budget-to-actual bridge

```
Budget → Volume variance → Price variance → Mix → Timing → Actual
```

Reconciles the gap in driver terms rather than line-item terms. Far more useful than a variance table
when someone asks "why did we miss".

---

## Multi-level and stacked bridges

**Stacked deltas** — when a single bar decomposes further (Expansion = upsell + cross-sell + price):
stack within the bar, keep the stack order consistent across bars, and only do this when the
sub-components are genuinely comparable. Beyond two or three sub-parts it becomes unreadable.

**Subtotal bridges** — for long walks, insert subtotals to create readable stages:

```
Opening → [operating deltas] → Subtotal: operating → [financing deltas] → Closing
```

**Nested bridges** — a summary bridge with a second bridge decomposing one bar. Excellent for
drill-down (`app-interaction-patterns`); the child bridge must foot to the parent bar exactly, and it
should say so.

---

## Encoding

**Sign.** Two colours plus position. Increases and decreases must differ in **lightness** as well as
hue, or the chart dies in greyscale. Never red/green alone.

**Direction is not goodness.** A rise in churn is an increase *and* bad. Decide whether the chart
encodes direction or goodness, then say which in the legend. Encoding both in one colour scale
guarantees a misread.

**Anchors are neutral** — grey or a muted tone. They are states, not changes, and colouring them like
deltas implies movement.

**Order** is chronological or logical, never sorted by size. A bridge sorted by magnitude is no
longer a walk.

---

## Labels

Every bar carries its value. A bridge the reader has to estimate from axis position has failed.

- **Value inside the bar** when it fits, **above/below** when it does not
- **Signed deltas** (`+180K`, `−38K`) — the sign is information
- **Anchors labelled with the absolute**, deltas with the change; never mix the two conventions
- Use `chartkit.avoidOverlap` for dense bridges — it preserves order while separating labels
- Add the **percentage of the opening** for context where it helps: `+180K (+3.9%)`

Category labels below the axis, rotated only as a last resort — rotated labels are a sign the chart
is too narrow or has too many bars.

---

## How many bars

Five to nine is the sweet spot. Beyond about twelve it stops being readable.

Too many? Group the small ones into "Other" — but **"Other" must be smaller than the smallest named
bar.** If it is not, you have hidden the story in a residual, which is a named anti-pattern. Break out
the next largest until that holds.

---

## Scale

- **Zero baseline**, unless every bar sits far from zero and the deltas would be invisible — in which
  case truncate deliberately, mark the axis break, and say so.
- Domain must include zero when any bar crosses it.
- For a bridge where deltas are tiny relative to the anchors, consider charting only the deltas with
  the anchors as text. The alternative is five bars of identical apparent height.

---

## Checklist

- [ ] `footing.ok` is true — checked in code, not by eye
- [ ] Connector lines present
- [ ] Every bar labelled with a signed value
- [ ] Anchors visually distinct from deltas
- [ ] Sign survives greyscale
- [ ] "Other" smaller than the smallest named bar
- [ ] Order is chronological or logical
- [ ] Title states the finding, not "ARR Bridge" (`chart-annotation`)
- [ ] As-of and period status present

---

## Related skills

- `svg-charting` — `waterfallLayout` and the footing check
- `chart-selection` — when a bridge is the right form
- `chart-annotation` — the takeaway title
- `variance-charts` — the budget-to-actual sibling
- `financial-tables` — the table that accompanies a bridge

