Chart for dashboards: line, area, bar (vertical), bar-horizontal, pie and donut. Hand-rolled SVG, theme-aware.
Data comes straight from Python rows: chart.data = rows (array of objects), x="mes" names the category/date
field, and chart.series = [{ key: "receita", label: "Receita" }, { key: "meta", label: "Meta", type: "line" }]
names the numeric fields. series may also be a list of keys (["receita", "despesa"], or the attribute
series="receita,despesa"); when omitted, every numeric field except x becomes a series.
Pie/donut use x as the slice label and the first series as the slice value.
Values: numbers or numeric strings; null / missing values leave a gap (no bar, a break in the line) and
don't count as zero. x values like "2026-03-01" or "2026-03-01T10:00" are dates: axis labels and the
tooltip format them (months when every date is the 1st). Money and percents: value-type="currency" /
"percent" (property valueType) formats the axis and the tooltip. Replace data to update
(the bars/lines animate to the new values); abLoad(chart, () => get_series(period)) adds loading,
error, retry and drops stale responses.
Several bar series are grouped side by side; add stacked to stack them, normalized for 100% stacked.
Hover/focus shows a tooltip with every series for that x; arrow keys move between points and the value is
announced. A hidden data table (and a "Ver dados" toggle when there is a heading) exposes the numbers to
screen readers. Loading, empty and error states built in. Colors: series color (tone or CSS color) or the
token palette --ab-chart-1..--ab-chart-8 (derived from --ab-accent and status tokens).
Waterfall (type="waterfall", e.g. an income statement / DRE): one row per bar, in the order given (never
sorted, even with x-type="date"). x is the label, the first series is the value (default: the value field)
and the row's kind field says what the value is:
- "change" (default when kind is missing): signed movement; the bar goes from the running balance to
balance + value (revenue up, expense down).
- "subtotal" / "total": the reported balance at that row (may be negative); the bar goes from zero to the
value, which becomes the running balance. It is drawn as given, never recalculated or added to the balance.
The balance starts at zero; to start elsewhere, make the first row a subtotal ("Saldo inicial").
chart.data = [{ label: "Receita bruta", value: 1000, kind: "change" }, { label: "Deduções", value: -100 }, { label: "Receita líquida", value: 900, kind: "subtotal" }, { label: "Custos", value: -600 }, { label: "Resultado", value: 300, kind: "total" }]. A missing, non-numeric or non-finite value (or an unknown
kind) is never read as zero: that row has no bar and is marked "Sem valor", and the balance becomes unknown,
so the following changes have no bar ("Saldo anterior desconhecido") until a subtotal/total with a value.
A zero is a valid value (a flat bar). Increases, decreases and subtotals/totals use --ab-ok, --ab-danger and a
neutral tone, with a legend, signed value labels, a tooltip (movement, previous and ending balance) and a
"Ver dados" table with the exact numbers. With many rows the plot scrolls horizontally; long labels wrap to two
lines. The exported waterfallSteps(items) returns the same from/to the chart draws.