Distribution and Relation
In the admin: a Dashboard › a widget › Edit
These widgets show where values concentrate: on a grid of two categories, on the days of the year, or as points placed by two numbers.


Heatmap
A grid of two categories, such as weekday × hour or branch × month, each cell colored by its value.
| Role | Takes | Unset |
|---|---|---|
| X | the columns, in the query's order (dates sorted) | the first column that isn't a number; a number, such as an hour, only by hand |
| Y | the rows, in the query's order (dates sorted) | the next column that isn't a number |
| Value | each cell's value | the first number column |
| Tooltip | more columns in a cell's tooltip | none |
When Acme's customers report problems, over the last 90 days:
SELECT to_char(reported_at, 'Dy') AS weekday,
to_char(reported_at, 'HH24') AS hour,
count(*) AS orders
FROM work_orders
WHERE reported_at >= now() - interval '90 days'
GROUP BY 1, 2, extract(isodow FROM reported_at)
ORDER BY extract(isodow FROM reported_at), 2
A missing cell stays empty. For zeros, build the grid with generate_series and LEFT JOIN the data to it.
| Setting | |
|---|---|
| Color | the scale's hue, from the background to it; unset, the theme's primary |
| Thresholds | fixed color bands instead of a scale, with their legend |
| Show values | each cell's number written in it |
| Format, Compact number, Show legend | how values read; the scale's legend |
| On click | what a click on a cell does |
HEATMAP
A grid of two categories (day × hour, branch × month), each cell colored by its value; a missing cell stays empty.
Size: 6 × 4 of the 12-column grid, at least 4 × 3 · Also: thresholds, events, export
| Data | Code | Takes | Required | Unset | |
|---|---|---|---|---|---|
| X | x | category, time | yes | first | The columns - in the query's order (a date sorted). Defaults to the first column that isn't a number; a number (an hour) only by hand. |
| Y | y | category, time | yes | first | The rows - in the query's order (a date sorted). Defaults to the next column that isn't a number; a number (a weekday) only by hand. |
| Value | value | number | yes | first | Each cell's value. Defaults to the first number column. |
| Tooltip | tooltip | category, number, time | none | More columns shown in a mark's tooltip, with their titles and formats. Optional. |
| Property | Code | Section | Values | |
|---|---|---|---|---|
| Color | color | appearance | The scale's hue, from the background to it - unset, the theme's primary. Thresholds draw fixed bands instead. | |
| Format | displayFormat | format | How its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's. | |
| Compact number | compact | format | Abbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's). | |
| Show values | showValues | appearance | default false | Each cell's number written in it. |
| Show legend | showLegend | appearance | default true | The series' (or slices') names, beside the chart. |
| Thresholds | thresholds | thresholds | Colors by value, in order: {color: "red"}, {from: 60, color: "amber"}, {from: 80, color: "green"} - the first, without from, the base. from: a number, {column} (the widget's row) or {variable} (a NUMBER or SELECT variable). Wins over Color. On a heatmap, bands of those colors with their legend. | |
| On click | click | events | What a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a heatmap, a cell. |
SELECT (ARRAY['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'])[d] AS weekday, lpad(h::text, 2, '0') AS hour, (random() * 100)::int AS readings FROM generate_series(1, 7) AS d, generate_series(0, 23) AS h ORDER BY d, h
Calendar heatmap
Every day of a year colored by its value: a year per row, as many of the latest three as fit. On a phone, the latest months that fit, with a notice when some are left out. Day and month names follow the viewer's language, and the week starts on the viewer's first day.
| Role | Takes | Unset |
|---|---|---|
| Date | the day | the first date column |
| Value | the day's value | the first number column |
| Tooltip | more columns in a day's tooltip | none |
Visits scheduled per day:
SELECT scheduled_start::date AS day, count(*) AS visits
FROM visits
WHERE scheduled_start >= now() - interval '1 year'
GROUP BY 1
ORDER BY 1
scheduled_start::date cuts days in the viewer's time zone. Its settings are a heatmap's: Color, Thresholds, Format, Compact number, Show legend and On click (a click on a day).
CALENDAR
Every day of a year colored by its value - a year per row, the latest three.
Size: 12 × 3 of the 12-column grid, at least 6 × 3 · Also: thresholds, events, export
| Data | Code | Takes | Required | Unset | |
|---|---|---|---|---|---|
| Date | date | time | yes | first | The day. Defaults to the first date column. |
| Value | value | number | yes | first | Each day's value. Defaults to the first number column. |
| Tooltip | tooltip | category, number, time | none | More columns shown in a mark's tooltip, with their titles and formats. Optional. |
| Property | Code | Section | Values | |
|---|---|---|---|---|
| Color | color | appearance | The scale's hue, from the background to it - unset, the theme's primary. Thresholds draw fixed bands instead. | |
| Format | displayFormat | format | How its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's. | |
| Compact number | compact | format | Abbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's). | |
| Show legend | showLegend | appearance | default true | The series' (or slices') names, beside the chart. |
| Thresholds | thresholds | thresholds | Colors by value, in order: {color: "red"}, {from: 60, color: "amber"}, {from: 80, color: "green"} - the first, without from, the base. from: a number, {column} (the widget's row) or {variable} (a NUMBER or SELECT variable). Wins over Color. On a calendar, bands of those colors with their legend. | |
| On click | click | events | What a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a calendar, a day. |
SELECT d::date AS day, (random() * 20)::int AS visits FROM generate_series(date '2026-01-01', date '2026-12-31', interval '1 day') AS d
Scatter and bubbles
Points placed by two numbers. With Size, bubbles; with Group, a series per group.
| Role | Takes | Unset |
|---|---|---|
| X | a number; a date by hand | the first number column |
| Y | a number | the next number column |
| Size | a bubble's size: its radius grows with the square root, from 6 to 40 pixels | none |
| Label | each point's name in its tooltip | the first column that isn't a number |
| Group | by hand: a series per value, colored by the column's presentation | none |
| Tooltip | more columns in a point's tooltip | none |
Each technician's orders against hours worked this month, a series per branch: choose branch for Group.
SELECT u.name AS technician,
count(DISTINCT te.work_order) AS orders,
round(sum(te.minutes) / 60.0, 1) AS hours,
b.code AS branch
FROM time_entries te
JOIN technicians t ON t.id = te.technician
JOIN totalbase.users u ON u.id = t."user"
JOIN branches b ON b.id = t.branch
WHERE te.started_at >= date_trunc('month', now())
GROUP BY u.name, b.code
| Setting | |
|---|---|
| Series | per Group value: its name and color; unset, the Group column's presentation |
| Axes | the X and Y axes' title, minimum, maximum, logarithmic scale and format; unset minimum fits the data |
| Reference lines | a line on the Y axis (or the X one) at a number, a statistic, a column or a variable; bands |
| Zoom | sliders under and beside the chart, the wheel or a pinch |
| Format, Compact number, Color, Show legend, On click | as other charts' |
width_bucket and draw them as a Bar chart.SCATTER
Points by two numbers - with Size, bubbles; with Group, a series per group.
Size: 6 × 4 of the 12-column grid, at least 3 × 3 · Also: series, thresholds, events, zoom, export
| Data | Code | Takes | Required | Unset | |
|---|---|---|---|---|---|
| X | x | number | yes | first | Defaults to the first number column; a date chosen by hand. |
| Y | y | number | yes | first | Defaults to the next number column. |
| Size | size | number | none | Each point's size (radius by its square root, 6 to 40 px) - bubbles. Optional. | |
| Label | label | category | first | Each point's name in its tooltip. Defaults to the first column that isn't a number. | |
| Group | group | none | A series per value of this column, colored by its presentation. Optional. | ||
| Tooltip | tooltip | category, number, time | none | More columns shown in a mark's tooltip, with their titles and formats. Optional. |
| Property | Code | Section | Values | |
|---|---|---|---|---|
| Format | displayFormat | format | How its numbers read - a displayFormat pattern: "$ #,##0", "#,##0.00", "0.0 %" (% never multiplies: 85 reads 85.0 %), "0.0 h", "CURRENCY:ARS". Unset: the value column's own (its presentation), else the locale's. | |
| Compact number | compact | format | Abbreviates large numbers - 11.6M, 2.4K - keeping the format's text and decimals ("$ #,##0" + compact: $ 1.3M). Unset: on, unless a format applies (its own or its column's). | |
| Color | color | appearance | A palette hue for its marks - unset, the theme's primary. | |
| Show legend | showLegend | appearance | default true | The series' (or slices') names, beside the chart. |
| Series | series | series | Per Group value: {label, color}. Unset: the Group column's presentation (its CHECK list's labels and colors). | |
| Axes | axes | axes | {x: {title, min, max, log, displayFormat}, y: {...}} - unset min fits the data. | |
| Reference lines | references | thresholds | Lines and bands on the value axis: {type: "line", value: a number | average | min | max | median | {column} | {variable}, label, color, series?} or {type: "band", from, to, label, color}. Labels read in the axis' format. On a scatter, axis: "x" or "y" (default y). | |
| On click | click | events | What a click on a mark does: {action: "FILTER", variable, value} sets a variable of this dashboard; {action: "DATAVIEW", dataview, filter} opens a DataView filtered (the where grammar); {action: "DASHBOARD", dashboard, variables, tab}; {action: "RECORD", dataview, pk}; {action: "URL", url, newTab, inEmbeds}. Values are formulas over (_ columns too) and ; targets by id. hint: the tooltip's last line. On a scatter, a point. | |
| Zoom | zoom | zoomExport | default false | Bars under and beside the chart, and the wheel or a pinch, to zoom into a range of x and y. |
SELECT * FROM (VALUES ('Ana', 12, 3500, 42000, 'GOLD'), ('Luis', 30, 2100, 63000, 'BLACK'), ('Marta', 7, 5200, 36400, 'GOLD'), ('Pedro', 18, 2900, 52200, 'PLATINUM')) AS t(customer, visits, avg_ticket, spent, level)
Next steps
- Maps: points on a street map.
- Editing a Widget: choosing a role's column by hand.