/// Charts
Heatmap
A hand-rolled grid heatmap: sharp cells whose fill opacity encodes value share of the domain max, monochrome by default.
import { Heatmap } from "@protocore/pds";Import path
Heatmap ships from the `@protocore/pds/charts` subpath. Unlike the recharts wrappers it is hand-rolled tokened markup — no recharts dependency — so it renders anywhere, but it is grouped with the charts family for its ramp conventions.
import { Heatmap } from "@protocore/pds/charts";
import { ChartContainer } from "@protocore/pds";Basics
Pass data as { x, y, value } cells plus the xLabels/yLabels axes. Each cell's opacity is its value's share of the domain max over a single hue — the ink chart-1 by default — so the grid reads in grayscale. Missing pairs render as a faint ghost cell.
When to use it
Use a Heatmap for a *dense two-way matrix* — activity by weekday × hour, errors by service × region, a calendar of daily counts. Intensity encodes magnitude by opacity, keeping the palette monochrome; switch color to a signal hue only when the whole grid measures one charged thing (errors, risk).
For a single ranked list of values use a BarsList; for a part-to-whole split use a PercentageBarChart.
Usage
Do
- Let opacity carry magnitude — keep to one hue per grid.
- Give short `xLabels`/`yLabels` so the axis headers stay legible.
- Set `max` explicitly to compare two heatmaps on the same scale.
- Provide a `label` (or rely on the composed one) for assistive tech.
Don't
- Don't colour cells by category — this is a sequential intensity, not a ramp.
- Don't round the cells or add shadows; keep the geometry sharp.
- Don't pack in so many columns that cells shrink below readability.
- Don't import it from `@protocore/pds`; it lives on the `/charts` subpath.
Accessibility
- The grid exposes `role="img"` with a composed aria-label (grid dimensions + max); override via `label`.
- Intensity encodes value by opacity independently of hue, so the matrix stays readable in grayscale and for colour-vision deficiencies.
- Each cell carries a `title` with its coordinates and value for pointer inspection.
Heatmap props
| Prop | Type | Default | Description |
|---|---|---|---|
data * | HeatmapCell[] | — | The cells to plot. `HeatmapCell` = `{ x: string; y: string; value: number }` — `x`/`y` must match `xLabels`/`yLabels`; missing pairs render empty. |
xLabels * | string[] | — | Column labels, left → right. |
yLabels * | string[] | — | Row labels, top → bottom. |
color | string | var(--pds-chart-1) | The hue whose opacity encodes intensity. |
cellSize | number | 20 | Cell edge length in px. |
minOpacity | number | 0.08 | Minimum fill opacity for a non-zero cell (the floor). |
max | number | largest value in data | Domain max; a cell's intensity is `value / max`. |
formatValue | (value: number, cell: HeatmapCell) => string | String(value) | Format a cell's value for its title/tooltip. |
label | string | auto-composed | Accessible summary (role=img aria-label). Composed from grid dimensions when omitted. |