Skip to content
Protocore Design Systemv1.6.9

/// 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";
View as Markdown

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.

Requests by day × hour

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

PropTypeDefaultDescription
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.
colorstringvar(--pds-chart-1)The hue whose opacity encodes intensity.
cellSizenumber20Cell edge length in px.
minOpacitynumber0.08Minimum fill opacity for a non-zero cell (the floor).
maxnumberlargest value in dataDomain max; a cell's intensity is `value / max`.
formatValue(value: number, cell: HeatmapCell) => stringString(value)Format a cell's value for its title/tooltip.
labelstringauto-composedAccessible summary (role=img aria-label). Composed from grid dimensions when omitted.

Related

  • BarsListA hand-rolled labelled horizontal bar list: each row is a mono label, a sharp value-share fill, and a tabular value.
  • PercentageBarChartA single 100%-stacked horizontal distribution bar with sharp, tokened segments and a swatch/label/percent legend.
  • ChartContainerThe bordered, theme-aware frame that hosts a chart and exposes the --pds-chart-1..5 ramp.