Heatmap
import { Heatmap } from '@absoluteui/components/heatmap';import '@absoluteui/components/heatmap/style.css';Using Angular? See Heatmap for Angular.
What it does
Section titled “What it does”An accessible SVG heatmap: a matrix of values over two categorical axes (columns along x, rows along y). Each cell’s fill encodes its value on a scale of discrete steps:
- sequential (default): one palette slot, light to dark;
- diverging: two palette slots on either side of a
midpoint.
Fill lightness alone is a weak cue, so it is never the only one:
- cells print their values when the formatted value fits (
cellLabels); - the legend lists every step’s value range;
- every cell is keyboard-reachable in two dimensions (one tab stop, arrow keys) with a tooltip, and its accessible name carries the exact value;
- the data table holds the exact matrix, and a generated summary names the size and the extremes.
Nothing animates.
When to use
Section titled “When to use”- Showing patterns across two categorical dimensions (weekday by hour, region by month, feature by segment).
- Diverging data around a meaningful midpoint (change from baseline, correlation): use colorScale.type ‘diverging’.
When not to use
Section titled “When not to use”- When exact comparison of a few values matters: use Bar Chart (fill lightness is a weak cue).
- Continuous x and y values: use Scatter Chart.
- Very large matrices (thousands of cells): aggregate first; every cell is a DOM element.
Examples
Section titled “Examples”Sequential heatmap with axis titles
Section titled “Sequential heatmap with axis titles”import { Heatmap } from '@absoluteui/components/heatmap';
const host = document.createElement('div');document.body.append(host);Heatmap.create(host, { label: 'Support tickets by weekday and hour', columns: ['9:00', '12:00', '15:00', '18:00'], rows: ['Mon', 'Tue', 'Wed'], values: [ [4, 12, 9, 3], [6, 15, null, 2], [5, 11, 8, 4], ], colorScale: { color: 3, steps: 4 }, xAxis: { label: 'Hour' }, yAxis: { label: 'Weekday' }, height: 240,});Diverging scale around zero with percent values
Section titled “Diverging scale around zero with percent values”import { Heatmap } from '@absoluteui/components/heatmap';
const host = document.querySelector<HTMLElement>('#change');if (host) { const chart = Heatmap.create(host, { label: 'Change from last year', columns: ['Q1', 'Q2', 'Q3', 'Q4'], rows: ['North', 'South', 'East'], values: [ [0.12, 0.05, -0.03, 0.08], [-0.1, -0.04, 0.02, 0.01], [0.03, 0.09, 0.15, -0.06], ], colorScale: { type: 'diverging', midpoint: 0, negative: 2, positive: 1, steps: 3 }, valueFormat: { style: 'percent', maximumFractionDigits: 0 }, dataTable: 'visible', }); chart.update({ cellLabels: 'none' });}import { Heatmap } from '@absoluteui/components/heatmap';import '@absoluteui/components/heatmap/style.css';
const host = document.querySelector<HTMLElement>('#load');if (host) { Heatmap.create(host, { label: 'Server load by day and hour', columns: ['00', '06', '12', '18'], rows: ['Sat', 'Sun'], values: [[0.2, 0.35, 0.8, 0.6], [0.15, 0.3, 0.7, 0.55]], valueFormat: { style: 'percent' }, colorScale: { min: 0, max: 1 }, });}More usage
Section titled “More usage”import { Heatmap } from '@absoluteui/components/heatmap';
const host = document.createElement('div');document.body.append(host);Heatmap.create(host, { label: 'Tickets by weekday and hour', columns: ['9', '12', '15'], rows: ['Mon', 'Tue'], values: [[1, 5, 9], [null, 4, 2]],});Anatomy
Section titled “Anatomy”<div class="aui-chart aui-heatmap [aui-chart--patterns]"> <div class="aui-chart__header">title, description</div> <ul class="aui-chart__legend" aria-label="Legend"><li><span class="aui-chart__legend-item">swatch, 0 to 2</span></li>...</ul> <div class="aui-chart__body"> <svg class="aui-chart__svg" role="group" aria-roledescription="heatmap" aria-labelledby="T" aria-describedby="S"> <g class="aui-chart__axis" aria-hidden="true">column and row labels</g> <g class="aui-heatmap__cells"> <rect class="aui-heatmap__cell aui-chart__series--c1 aui-heatmap__level--60 aui-chart__mark" role="img" aria-label="12, Tue: 4" tabindex="0"/> <rect class="aui-heatmap__cell aui-heatmap__cell--empty aui-chart__mark" role="img" aria-label="9, Tue: No value"/> </g> <g class="aui-heatmap__labels" aria-hidden="true"><text class="aui-chart__value-label">4</text>...</g> </svg> <div class="aui-chart__tooltip" aria-hidden="true" hidden/> </div> <p class="aui-chart__summary" hidden/> <div class="aui-chart__data">Show data table, table</div></div>API reference
Section titled “API reference”Factory
Section titled “Factory”Heatmap.create(host: HTMLElement, options: HeatmapOptions): HeatmapGlobal configuration key: heatmap (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Chart title: visible text and the accessible name of the plot (aria-labelledby); also the data table caption. Required. | |
hideLabel |
boolean |
false |
Visually hide the title; it stays the accessible name. |
description |
string |
Visible description below the title, also referenced by aria-describedby (text only). | |
summary |
string |
Replaces the generated accessible summary (matrix size, lowest and highest value with their cells). | |
valueFormat |
Intl.NumberFormatOptions |
Number format for cell labels, legend step ranges, tooltips, the data table, and the summary. | |
formatValue |
(value: number) => string |
Custom value text; overrides valueFormat everywhere. | |
hiddenSeries |
readonly string[] |
Shared chart option; a heatmap has no series, so it has no effect. | |
columns |
readonly string[] |
Column labels (x axis), in order (mirrored in RTL). Required. | |
rows |
readonly string[] |
Row labels (y axis), top to bottom. Required. | |
values |
readonly (readonly (number | null)[])[] |
values[row][column]: exactly one array per row and one value per column. null, NaN, and infinities are empty cells (“No value”). Required. | |
colorScale |
HeatmapColorScale |
{ type?: ‘sequential’ | ‘diverging’ (default sequential), color?: 1-8 (sequential slot, default 1), negative?: 1-8 (default 2), positive?: 1-8 (default 1), midpoint?: number (diverging center, default 0), min?, max? (fixed domain; default the nice-rounded data extent), steps?: 3-9 (per side for diverging, default 5) }. Values outside a fixed domain use the first or last step. | |
xAxis |
{ label?: string } |
Column axis title (drawn below the plot). | |
yAxis |
{ label?: string } |
Row axis title (drawn beside the row labels); also the header of the data table’s first column (default “Row”). | |
legend |
'auto' | 'show' | 'hide' |
"auto" |
Legend listing every color step with its value range; auto and show both show it, hide removes it. Entries are not toggle buttons. Globally configurable. |
dataTable |
'toggle' | 'visible' | 'hidden' | 'none' |
"toggle" |
Data table fallback with the exact matrix: behind a Show data table button (built on first open), always visible, visually hidden (screen readers only), or none. Globally configurable. |
tooltip |
boolean |
true |
Tooltip on pointer hover and keyboard focus (cell name and value). Globally configurable. |
patterns |
boolean |
false |
Shared chart option (toggles aui-chart–patterns); heatmap cells and step swatches are not textured, so it has no visible effect. Globally configurable. |
height |
number |
300 |
Plot height in CSS px (integer 120-2000); the width follows the container. Cells share the plot evenly. Globally configurable. |
cellLabels |
'auto' | 'always' | 'none' |
"auto" |
Values printed inside cells: auto and always print them where the formatted value fits (always skips cells that are too small); none prints no values. Globally configurable. |
locale |
string |
"en-US" |
Number and message locale. Globally configurable. Inherited from common configuration. |
direction |
'ltr' | 'rtl' |
"ltr" |
rtl mirrors the columns (first column at the right), puts the row labels on the right, and swaps ArrowLeft/ArrowRight. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
The host element. |
element |
SVGSVGElement |
The plot (svg role=“group”), named by the title and described by the summary. |
summary |
string |
The generated (or custom) accessible summary. |
hiddenSeries |
string[] |
Shared chart property; always empty for a heatmap. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
update(options: Partial<HeatmapOptions>): void |
Merge, validate, and re-render. Invalid options throw and keep the previous state. Keyboard focus stays on the same cell. |
setSeriesVisible(id: string, visible: boolean): void |
Shared chart method; a heatmap has no series, so it only re-renders. |
destroy(): void |
Remove rendering, host classes, listeners, the resize observer, and the configuration subscription. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-chart |
Host (shared chart chrome). |
aui-heatmap |
Host modifier for this chart type. |
aui-chart--patterns |
patterns option on (no visible effect on heatmap cells). |
aui-chart__header / __title / __title--hidden / __description |
Title and description. |
aui-chart__legend / __legend-entry / __legend-item / __legend-label |
Legend of color steps (static entries, not buttons). |
aui-chart__swatch |
aria-hidden legend and tooltip key (step fill). |
aui-chart__body / __svg / __layer |
Plot container (height from –aui-chart-height), SVG, and drawing layer. |
aui-chart__axis / __tick-label / __axis-title |
Column and row labels and axis titles (aria-hidden). |
aui-chart__series--c1 ... --c8 |
Palette slot classes; set –aui-chart-color from color-chart-<n>. |
aui-heatmap__level--15 ... --100 |
Fill strength in steps of 5: the palette color mixed into color-surface at that percentage (sets –aui-heatmap-fill). |
aui-heatmap__cells / __cell / __cell--empty |
Cell group, focusable cells (also aui-chart__mark), and empty (null) cells. |
aui-heatmap__labels |
aria-hidden group of printed cell values (aui-chart__value-label). |
aui-chart__mark / __mark--active / __mark--hover |
Focusable cell and its states (thicker color-text outline). |
aui-chart__value-label |
Printed values with a surface-colored halo. |
aui-chart__tooltip / __tooltip-title / __tooltip-rows / __tooltip-row / __tooltip-label / __tooltip-value |
Tooltip (aria-hidden; the focused cell carries the same text). |
aui-chart__empty |
Empty state text (No data). |
aui-chart__summary |
Hidden summary referenced by aria-describedby. |
aui-chart__data / __data--hidden / __table-toggle / __table-scroll / __table / __cell--numeric |
Data table fallback. |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-chart-axis | --aui-color-chart-grid | --aui-color-focus-ring | --aui-color-hover | --aui-color-link | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-medium | --aui-font-weight-regular | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-tooltip-max-width | --aui-touch-target-min
TypeScript definitions
Section titled “TypeScript definitions”type HeatmapCellLabels = 'auto' | 'always' | 'none';interface HeatmapConfig extends ChartBaseConfig { cellLabels: HeatmapCellLabels }interface HeatmapColorScale { type?: 'sequential' | 'diverging'; // default 'sequential' color?: ChartColor; // sequential slot, default 1 negative?: ChartColor; // diverging, below midpoint, default 2 positive?: ChartColor; // diverging, above midpoint, default 1 midpoint?: number; // default 0 min?: number; // default: data extent (nice-rounded) max?: number; steps?: number; // 3-9 (per side for diverging), default 5}interface HeatmapOptions extends ChartCommonOptions, Partial<HeatmapConfig> { columns: readonly string[]; rows: readonly string[]; values: readonly (readonly (number | null)[])[]; colorScale?: HeatmapColorScale; xAxis?: { label?: string }; yAxis?: { label?: string };}Accessibility
Section titled “Accessibility”Pattern: SVG matrix chart as a named, described group (role=“group” with aria-roledescription “heatmap”) of role=“img” cells with two-dimensional roving focus; a legend listing every color step with its value range; data table fallback with the exact matrix.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| ArrowLeft / ArrowRight | Cell focused | Previous / next column in the same row (swapped in RTL). |
| ArrowUp / ArrowDown | Cell focused | Same column in the previous / next row. |
| Home / End | Cell focused | First / last cell of the row. |
| PageUp / PageDown | Cell focused | Ten columns back / forward in the row (clamped to the ends). |
| Escape | Tooltip visible | Hides the tooltip without moving focus (WCAG 1.4.13); it returns on the next move. |
| Tab / Shift+Tab | Any | One tab stop for the plot (the active cell), then the Show data table button and table region. Legend entries are not focusable. |
| Element | Role |
|---|---|
| svg.aui-chart__svg | group with aria-roledescription “heatmap” (localized), named by the title |
| cells (rect.aui-heatmap__cell) | img with an aria-label: column, row, formatted value (or “No value”) |
| legend | list (ul) named “Legend”; entries are static text (step ranges), not buttons |
| data table | table (native) with caption, column headers (row axis title, then the columns), and row headers; wide tables scroll in a focusable region |
| row and column labels, axis titles, printed cell values, tooltip | aria-hidden (decorative duplicates of the accessible data) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-labelledby |
svg | id of the visible (or visually hidden) title |
aria-describedby |
svg | description id (when provided) and the generated summary id |
aria-roledescription |
svg | Localized “heatmap” |
aria-label |
each cell | Localized “{column}, {row}: {value}”, for example “12, Tue: 4”; empty cells read “No value” |
tabindex |
active cell | 0 on exactly one cell (roving); other cells have none |
aria-expanded / aria-controls |
Show data table button | Table open state and the table region id |
Roving focus: one cell is in the tab sequence and remembers the last visited cell, also across re-renders (resize, update, configuration changes). Every cell, including empty ones, is reachable. Toggling the table keeps focus on the table button.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- Fill lightness is a weak cue (and sequential steps of one hue are hard to tell apart), so it is never the only one: cells print their values when they fit (cellLabels), the legend lists every step’s value range, each cell’s accessible name and tooltip carry its exact value, and the data table holds the whole matrix.
- The generated summary names the matrix size and the lowest and highest values with their cells, for example “heatmap, 2 rows by 3 columns. Lowest 1 at 9, Mon; highest 9 at 15, Mon.”
- Cells are separated by a color-surface stroke, so neighboring fills need no mutual contrast; the hovered or focused cell gets a 2px color-text outline.
- Forced colors: fills are not reliable, so cells become outlined boxes (Canvas fill, CanvasText stroke; Highlight for hover and focus); values remain available through the printed cell labels, accessible names, and the data table.
- No animation.
- Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.
- aria-roledescription support varies; screen readers without it announce “group”.
- Large matrices mean many arrow presses; browse-mode users can read the data table instead.
Behavior
Section titled “Behavior”States
Section titled “States”default (sequential), diverging, empty (“No data”: no rows, no columns, or no finite values), empty cell, cell focused (tooltip), cell hovered, table open.
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| plot not focused | Tab | active cell focused, tooltip shown |
| cell focused | arrow keys, Home, End, PageUp, PageDown | another cell focused |
| tooltip visible | Escape | tooltip hidden |
| any | update() / configuration change |
re-rendered; focus kept on the same cell |
Accessibility notes
Section titled “Accessibility notes”- Trade-off: lightness steps of one hue are hard to tell apart, and color alone would fail WCAG 1.4.1. The heatmap therefore prints values in cells that fit them, lists each step’s range in the legend, exposes every value through the cell names and tooltips, and offers the exact matrix in the data table. Keep at least
cellLabelsordataTableon. - Every cell (including empty ones) is keyboard-reachable in two dimensions.
- The summary names the size and extremes: “heatmap, 2 rows by 3 columns. Lowest 1 at 9, Mon; highest 9 at 15, Mon.”
- Cells are separated by a
color-surfacestroke, so neighboring fills need no mutual contrast. - Forced colors: fills are not reliable, so cells become outlined boxes (
Canvas/CanvasText,Highlighton hover and focus); values stay available through the printed labels, accessible names, and the data table. - No animation. Manual screen-reader verification is pending.
Responsive behavior
Section titled “Responsive behavior”The width follows the container (ResizeObserver); the height is height. Cells share the plot evenly. Row labels take at most 30% of the width and are truncated; column labels are truncated or thinned when crowded; cell values are printed only where they fit. Reflows at 320 CSS px.
Right-to-left
Section titled “Right-to-left”direction: 'rtl' puts the first column at the right and the row labels on the right, and swaps ArrowLeft/ArrowRight. The legend, tooltip, and table follow the document direction.
Theming
Section titled “Theming”Step fills mix color-chart-<slot> into color-surface at 15-100% (color-mix), so they adapt to every theme; empty cells use color-surface-sunken. Lighter steps are intentionally low-contrast: meaning is carried by the printed values, legend, and table, not by the fill.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { heatmap: { cellLabels: 'none', dataTable: 'visible' } } });Changes apply to existing instances immediately.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic.
Security
Section titled “Security”All text uses textContent; SVG uses createElementNS; colors are classes. No innerHTML or style attributes.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry.
Performance
Section titled “Performance”Budget: Heatmap.create with 24 x 30 cells < 60 ms in headless Chromium (e2e performance test), including the first render. One number formatter per render; the data table is built lazily.
Supported data scale
Section titled “Supported data scale”Up to about 1,000 cells; every cell is a focusable rect.
Edge cases
Section titled “Edge cases”null,NaN, and infinities are empty cells (color-surface-sunken, “No value”); they are still focusable.- All cells empty, or no rows or columns: “No data”.
- All values equal: every cell uses the same step.
- Values outside a fixed
colorScale.min/maxuse the first or last step; the legend then reads “Below …” / “Above …” at the ends. - Diverging scales are symmetric around
midpoint(the larger distance sets both sides); the legend has2 x stepsentries.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
HEATMAP_OPTIONS |
configuration | Missing label; non-string columns or rows; values not one array per row with one number-or-null per column; invalid colorScale (type, slots, non-finite numbers, max <= min, steps outside 3-9); invalid axis titles or formatters. |
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION |
configuration | Invalid or unknown configuration. |
A failed construction leaves the host untouched.
Known limitations
Section titled “Known limitations”- Discrete steps only; one slot (sequential) or two (diverging).
cellLabels: 'always'prints values only where they fit, like'auto'.- Manual screen-reader verification is pending.
Unsupported scenarios
Section titled “Unsupported scenarios”- Numeric or time axes, uneven cells, calendar layouts.
- Clustering, sorting, selection.