heatmap — Lattice component

heatmap

EvidenceCanvasSeries

A numeric matrix read as intensity — where the value concentrates across two dimensions.

Open in Playground
Variant

Use when the question is 'where does this concentrate', not 'how do these compare'. Every row-by-column crossing carries a number, and one sequential ramp turns the whole grid into a shape a reader takes in at once — a retention cliff, a risk cluster, a quiet quarter.

metricpercentageassessmentpositioning

When to use

  • Two dimensions, one measure. A number exists at every crossing of two categorical axes — cohort by month, region by quarter, hour by weekday — and the finding is a PATTERN across the grid rather than any single value.
  • The shape is the story. You want the reader to see a cliff, a diagonal or a cluster before they read a single number. Intensity carries that pre-attentively; a table of the same numbers does not.
  • A ragged matrix is honest. Newer cohorts genuinely have no month-six value yet. A heatmap paints those crossings as unmeasured rather than as zero, so the gap is visible instead of being read as a collapse.

When not to use

  • One row of numbers. A single series is not a matrix — it is a comparison, and a reader judges length far more precisely than intensity. Use bar. The kernel declines a one-column table for this reason rather than painting a single strip.
  • Qualitative cells. If the cells are verbs, owners or statuses rather than numbers, the ramp has nothing to encode. Use matrix-grid, whose cells are tagged at parse time.
  • Precise comparison. Asking a reader which of two similar cells is larger spends the one thing intensity is bad at, and the five-tone binning above makes it stricter: two cells in the same band are the same color by design. If the comparison has to be exact, the value belongs on an axis — bar or line.
  • Geography. A value per country or region belongs on the map choropleth, which shares this ramp but places the cells where the reader expects them.

Common mistakes

  • Putting every value in one column. A heatmap needs two dimensions. The header row names the columns and each body row leads with its label; one value column is a comparison, not a matrix, and renders nothing deliberately.
  • Padding a ragged matrix with zeros so every cell is filled. Leave the cell blank. A zero states a measurement; an absent cell states that none was taken, and the chart paints the two differently.
  • Mixing units down a column — 62% in one row and 0.62 in the next. One measure, one unit, across the whole grid. The ramp is a single scale and cannot describe two.

Authoring

<!-- _class: heatmap -->

## Where it concentrates.

|  | Col A | Col B |
| --- | --: | --: |
| First row | 12 | 34 |
| Second row | 21 | 43 |

Slots

SlotSelectorRequiredDescription
titleh2yesSlide heading — name what the grid shows ('Retention decays fastest in month two'), not the chart type.
eyebrowp > code:only-childnoOptional kicker above the title — the dataset and its period.
rowstable > tbody > tryesOne table row per ROW of the matrix, leading with its label. The header row names the columns; a blank cell is a crossing nobody measured.

Data shape

  • A markdown table: the header names the columns, each body row leads with its label.
  • A blank cell is a crossing nobody measured, and paints as one. That is the normal case for a ragged matrix, not an authoring error.
  • Up to 12 columns and 10 rows. Past that the cells stop carrying a readable value; consolidate a long tail rather than shrinking the grid.
  • One unit across the whole matrix. The affix is read from the values and printed once in the description.
  • Values are binned into five tones, so near-equal cells share one. The ramp is quantized rather than continuous — that is what lets the printed value keep AA contrast on every palette. The bands are cut at the matrix's own quantiles, so a skewed table still uses the whole ramp, but tone is not linear in value: the number in the cell is exact, the tone is a band. If a reader has to tell two close values apart by color, the comparison belongs on an axis.
  • The band key is OPT-IN. Add scale to the slide and the chart names each of the five ramp bands by the range of values that band actually holds. It sits beside the grid on a wide slide and under it on a squarer one, whichever leaves the grid larger.
  • To name the bands yourself, write a label set in an inline-code span above the table: [{1, Cold}, {3, Warm}, {5, Hot}]. The KEY IS THE RAMP STEP, 1 through 5 — not a value from your data. Name only the steps that carry meaning; the rest keep their derived range.
  • The ramp is cut at the matrix's own quantiles, so which step a value lands in depends on the whole matrix. If a step you named carries no cells, its label is dropped and the description says so — check the rendered key rather than assuming step 2 is 'the second-smallest value'.
  • To point at one cell, add a callout with the callout plugin: a paragraph holding @callout{{Onboarding change, row="Feb 2026", col=M2}} in inline code under the table. row= and col= name the cell by its labels as the chart prints them; the short form for= takes its index counted row by row from 0 (for=14) or Row/Column (for=Feb 2026/M2). The label sits in a lane to the right of the grid or in a column under it, whichever leaves the grid larger at the slide's shape, and a marker ties it to its cell: the cell is ringed and carries a marker, and the same marker leads the label. A marker is one of six shapes (circle, square, triangle, diamond, hexagon, star) in a color, one shape per callout, with its number inside when the cell has room; on a grid whose values fill their cells the shape goes alone. No line crosses the grid, and a marker goes only where it covers no printed number or name: whole inside the cell's top-left corner, or as a badge just outside it, the same place and size for every marker on the chart. The pill's words pick the marker ({…, tag, c4} is a square in c4); it is sized from the cell and never larger than its copy in the lane. A callout row is styled like a key row: its marker is the key's swatch size, and its words take the key's typeface, weight and size. At most six callouts draw on one chart; the rest are listed as not drawn and lint:deck warns. detail="…" joins the cell's card and the speaker note. Several callouts go in a list, @callout{[{A, for=…}, {B, for=…}]}. A lane beside the grid takes at most a quarter of the figure's width; a column under it takes none. callout: off in the front matter (or callout-off on the slide) hides callouts; callout-on brings a slide's back. A pre-split @legend{…} span still draws. Contract: lib/plugins/callout/callout.docs.md.
  • A cell, a column and a row can carry a description, written {> prose} in a code span. After a cell's value it describes the cell — | Jan | 100 | 62 {> dipped after onboarding} | — and shows on hover on the live surfaces, folds into the slide's speaker note for print, and never changes the value. A description on an empty cell is kept, and the cell stays unmeasured. In a header cell it describes the column (M1 {> the second month}`), and a column whose header is only {>} holds one plain-text description per row; that column is no data column and is taken out before the 12-column cap. Column and row descriptions are spoken and joined to the chart's text description. The prose runs to the first } (a comma, = or quote in it is prose; write \} for a brace); a | in it must be written \| in a table. The older note=dipped` annotation still reads and is rewritten with the other old spellings in phase 4.

Variants

scale

Renders the band key beside the grid, naming each ramp step by the range of values it covers — reach for it when a reader has to know why two cells share a tone.

<!-- _class: heatmap scale -->

`Retention · 2026 cohorts`

## The chart names its own bands.

|  | M0 | M1 | M2 | M3 |
| --- | --: | --: | --: | --: |
| Jan 2026 | 100 | 62 | 48 | 44 |
| Feb 2026 | 100 | 58 | 44 | 41 |
| Mar 2026 | 100 | 71 | 59 | 55 |
| Apr 2026 | 100 | 69 | 57 |  |

Related

  • matrix-gridthe cells are qualitative — a verb, an owner, a status — rather than a number
  • mapthe two dimensions are geographic; same ramp, spatial placement
  • barone dimension, and the comparison has to be exact
  • quadranttwo CONTINUOUS axes with items placed on them, rather than two categorical axes with a value at each crossing