Dashboard & charts
We wrap a charting engine (Recharts, or visx where more control is needed). We do not rebuild one. What we add is everything a chart library doesn't know about money.
The governing rules: Money Law 2 — a chart animates on first paint and never on data change. And every chart has an accessible table equivalent.
71. DashboardShell
12/8/4-column responsive grid with optional user rearrangement. Widget positions persisted per user. Drag uses FLIP + spring.snap, measured once at drag start.
Equal-height rows via auto-rows-fr so figure baselines align across a row — the most visible alignment failure on any dashboard, and it's a one-line fix.
72. KpiRow
2–4 KpiCards with a shared figure baseline. Forces Inter on all figures regardless of the family prop, because these align across the row. Enforced by the parent, not left to the caller.
73. MoneyChart
Line and area, with everything a generic chart library gets wrong about money:
- Money-formatted axis — abbreviated (
$1.2M) with the exact value in the tooltip. Abbreviation is licensed here; see numerics. - Zero baseline is emphasized — a 1px
--border-strongline, distinct from the--border-subtlegridlines. When data crosses zero, that line is the most important thing in the chart. - Negative regions are visually distinct — a fill below zero at
--negativewith a 45° hatch, so it survives greyscale. - Y-axis starts at zero for quantities. The only sanctioned exception is a rate chart (
CurrencyPairChart), and there the axis is explicitly labeled. - Projections are dashed. A forecast rendered identically to actuals is a lie. The boundary itself is drawn — a dashed vertical rule labelled
Forecast →— because a dash pattern read across six months is not a date. - Gridlines: horizontal only,
--border-subtle. Vertical gridlines on a time series are noise. - Area fill is the one sanctioned gradient — a single hue fading to transparent, because it keeps the line readable over gridlines. Legibility, not decoration.
- Multi-currency series is rejected at the type level. Convert to a stated presentation currency first, and label the axis with it.
The FP&A layer
A plan chart has to say four things a line chart cannot, and each of them is a way of being wrong that a prettier chart hides.
- The range, and what it is.
bandtakes a low and a high and a requiredlabel—P10–P90,±1σ,Downside to upside. A shaded cone with no stated interval is a precision claim nobody made, and a reader who cannot tell which interval they are looking at will assume whichever suits them. The band is bounded by a hairline as well as shaded, because a fill alone disappears on a projector, and the edges are the part people quote. It widens with distance or it is lying about what is knowable. - The alternatives, unranked.
scenariosdraws base, upside and downside from the same anchor, told apart by dash rhythm first and colour second — a base/upside/downside triple in red-amber-green is three lines a third of readers cannot separate. No best-case styling and no ordering by outcome: a scenario is a set of assumptions, and presenting one as the good one is how a forecast review becomes a negotiation about which line to show the board. - The plan.
planis a reference, not a series: dotted, in the neutral tertiary tone, taking no viz colour and absent from the legend's isolate control. A budget is the line the business agreed to, not another thing that happened, and giving it a series colour puts it in competition with the measure it exists to be read against. - The variance, in words that survive greyscale. Stated as F and U rather than as a colour or a bare sign. Favourable and unfavourable are the terms of art, they are unambiguous under polarity — a cost under plan is F and revenue under plan is U, and both are negative absolute variances — and no amount of red and green carries that.
plan.polarityis required for the same reason it is required everywhere else: a chart does not know its own polarity, and guessing one gets half of FP&A backwards.
The variance is always taken against the first series declared, and the readout disappears when that series is isolated away. A variance whose base silently changes as the reader filters is worse than no variance: the number moves and nothing on the chart says why.
The band, the scenarios and the plan are all inside the y-domain. A cone clipped by the axis is a chart that has quietly cropped the bad case out of the picture.
The table equivalent carries all of it — a Basis column marking each period actual or forecast, the band's range, each scenario, the plan and the variance. A table listing the base values alone is not the chart in another form; it is a different, more confident chart.
Motion: stroke-dashoffset draw, 400ms --ease-out, once on mount or scroll-into-view. Instant on data change.
Touch: 44px-tall scrub target across the plot, with a persistent readout in a fixed header — not a floating tooltip a finger covers.
74. MoneyBarChart
Grouped and stacked.
transform-originat the zero baseline, not the bottom. Otherwise negative bars grow upward from the floor and briefly display the wrong sign — a real correctness issue in an animation.- Negative bars hatched.
- Stacked totals labeled at the bar end.
- Bar radius 4 on the outer corners only, and square below 12px bar width — a rounded 6px bar is a blob.
- Draw-in:
scaleY 0→1, 400ms, stagger 30ms capped at 240ms. First paint only.
75. WaterfallChart
The P&L bridge. Essential in finance, and almost nowhere in open-source charting.
Start → increases → decreases → end, with connector lines between bars. Increases --positive, decreases --negative, subtotals neutral — and all three distinguished by position and connector, not color alone.
The most useful chart in the family: it's how you answer "why did net income change?" in one image.
76. CategoryBreakdown
Ranked horizontal bars with values and percentages. Not a pie chart. Humans compare lengths accurately and angles poorly; a pie with more than three slices is decoration.
Top N + "Other" (max 6 series). Percentages use largest-remainder allocation so they sum to exactly 100%.
77. CashCalendar
Daily cash movement as a heatmap. Sequential OKLCH lightness ramp within one hue for magnitude, with the diverging in/out distinction carried by two separate ramps that differ in lightness, not by red/green.
78. CandlestickChart
For FX and crypto pairs. Up and down candles differ in fill (hollow vs. solid) as well as color — the traditional convention, and it happens to be the correct accessibility answer.
79. ChartLegend
Interactive: click to isolate a series, keyboard-navigable, aria-pressed state. Positioned below on phone as a wrapping row. Series colors are assigned by a stable key hash, never by array index, so a category is the same color in the dashboard and the drill-down.
80. ChartTooltip
Full values, never abbreviated. All series at the hovered point, in the same order as the legend. Touch-persistent variant. Follows the pointer with no animation — a tooltip that eases behind the cursor feels broken.
81. DataTableToggle
Every chart has an accessible table equivalent, reachable by a visible toggle.
This is a WCAG requirement, and it's also the feature power users want most — the actual numbers. Serving both with one component is the reason it's P1 and not P3.