# Heatmap Chart

> A matrix of values, each cell a level of one colour, where two categories meet.

Source: https://ngwr.dev/charts/heatmap-chart  
Added in v15.1

## Installation

Import the class, and `@use` the entry point in your global styles: without it the component renders unstyled.

```typescript
import { Component } from '@angular/core';
import { WrHeatmapChart } from 'ngwr/charts/heatmap-chart';

@Component({ imports: [WrHeatmapChart] })
export class MyComponent {}
```

```scss
@use 'ngwr/charts/heatmap-chart';
```

## Basic

One array per row, one value per column. The scale runs from the lowest value to the highest, cut into five levels.

```angular-html
<wr-heatmap-chart
  [rows]="['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']"
  [columns]="['08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00']"
  [data]="visits"
  ariaLabel="Visits by weekday and time"
/>
```

```angular-ts
visits: WrHeatmapMatrix = [
  [12, 28, 46, 31, 27, 44, 19], // Mon
  [14, 30, 49, 33, 29, 47, 21], // Tue
  // …one array per row, one value per column
];
```

## Values

`showValues` prints each value in its cell, in black or white, whichever reads better there. A value wider than its cell is left out, never cut.

```angular-html
<wr-heatmap-chart
  [rows]="days"
  [columns]="slots"
  [data]="visits"
  ariaLabel="Visits by weekday and time"
  showValues
/>
```

## Scale

`min` and `max` hold the scale, so two charts read the same. `levels` sets how many shares it is cut into, from 2 to 10.

```angular-html
<!-- Held at 0 to 100, in four levels -->
<wr-heatmap-chart
  [rows]="teams"
  [columns]="months"
  [data]="resolved"
  [min]="0"
  [max]="100"
  [levels]="4"
  [valueFormat]="percent"
  ariaLabel="Tickets resolved within a day, by team and month"
  showValues
/>
```

```angular-ts
percent = (value: number) => `${value}%`;
```

## Missing values

A `null` cell is no data, not zero: it is hatched and outlined, and the legend keys it. `noDataLabel` names it.

```angular-html
<wr-heatmap-chart
  [rows]="stores"
  [columns]="days"
  [data]="sales"
  ariaLabel="Sales by store and day"
  noDataLabel="Closed"
  showValues
/>
```

```angular-ts
sales: WrHeatmapMatrix = [
  [42, 38, 45, 51, 63, 71, 34],
  [55, 52, 58, 60, 74, 66, null], // closed on Sunday
  [31, 35, 33, 36, 40, null, 18], // closed on Saturday
];
```

## Many columns

Names wider than their columns are thinned to every second, third or later one. A screen reader, the tooltip and the table still get every name.

```angular-html
<wr-heatmap-chart [rows]="days" [columns]="hours" [data]="orders" ariaLabel="Orders by weekday and hour" />
```

```angular-ts
hours = Array.from({ length: 24 }, (_, h) => `${String(h).padStart(2, '0')}:00`);
// 00:00 … 23:00, drawn as every second or third name where they do not fit
```

## Colour

`color` is the highest level. Every lower level is mixed from it toward the surface.

```angular-html
<wr-heatmap-chart
  [rows]="stores"
  [columns]="days"
  [data]="sales"
  color="var(--wr-color-success)"
  ariaLabel="Sales by store and day"
  noDataLabel="Closed"
  showValues
/>
```

## Data table

`dataTable` adds a table only screen readers reach: a row per row, a column per column. Nothing changes on screen.

```angular-html
<wr-heatmap-chart [rows]="days" [columns]="slots" [data]="visits" ariaLabel="Visits by weekday and time" dataTable />
```

## Accessibility

| Part | Behaviour |
| --- | --- |
| Cells | One `role="img"`, named by `ariaLabel`. The cells announce nothing. |
| Value | Colour is the only thing that encodes it, as in every heatmap. `showValues` and `dataTable` give it in text. |
| Equal values | One level, the lowest, and one swatch in the legend: an all-zero matrix is a faint tint, not the peak. |
| Empty cell | Hatched and outlined, so it differs from the lowest level without colour. |
| Row and column names | Real text. Column names that do not fit are thinned to every n-th, and the rest stay readable to a screen reader. A long row name is cut with an ellipsis. The tooltip and the table carry every name whole. |
| Legend | Hidden from screen readers, like an axis. The table has the figures. |
| Keyboard | No tab stop. The tooltip is pointer only. |
| RTL | The matrix mirrors, like a table: the row names move to the right. |

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `data` | The values, rows × columns: one array per row, in the order of `rows`, each with one value per column, in the order of `columns`. A `null`, a value that is not a finite number, or one past the end of a shorter row is an EMPTY cell — no data, drawn hatched and outlined rather than as the lowest level. | `WrHeatmapMatrix` | `[]` |
| `rows` | The rows' names, top to bottom, beside the matrix at inline start. The matrix is as tall as the longer of this list and `data`; a row with no name here is named by its position, from 1. A name wider than `--wr-heatmap-chart-row-label-max-width` (10rem) is cut with an ellipsis, and the tooltip and the data table carry it whole. | `readonly string[]` | `[]` |
| `columns` | The columns' names, from inline start, above the matrix. The matrix is as wide as the longer of this list and the longest row of `data`; a column with no name here is named by its position, from 1. Where the names are wider than their columns, the chart draws every second, third or later one and leaves the rest out of sight, still read by a screen reader; the tooltip and the data table name every column. A single column too narrow for its name cuts it with an ellipsis. | `readonly string[]` | `[]` |
| `color` | The colour of the highest level: any CSS colour, `var(--wr-…)` included. Every lower level is mixed from it toward the surface, the lowest a faint tint. | `string` | `'var(--wr-color-primary)'` |
| `min` | The value of the lowest level. `null` takes the lowest value in `data`; a value given holds the scale there, and a cell below it takes the lowest level. | `number \| null` | `null` |
| `max` | The value of the highest level. `null` takes the highest value in `data`; a value given holds the scale there, and a cell above it takes the highest level. Bounds given in the wrong order are swapped. A scale with no span — every value the same, or `min` equal to `max` — has one level, the lowest: every value is drawn on it, an all-zero matrix as a faint tint rather than as the peak, and the legend keys that one level. | `number \| null` | `null` |
| `levels` | How many levels the scale is cut into, each an equal share of it from `min` to `max`: whole numbers from 2 to 10. Fewer levels are easier to tell apart; the value text and the data table carry what the levels round. | `number` | `5` |
| `showValues` | Print each value in its cell, compact from a thousand (`1.2K`). A value is printed whole or not at all: one wider than its cell is left out rather than cut, and the tooltip and the data table still carry it. Black or white, whichever reads better on the cell — picked with CSS relative colour syntax; a browser without it (Firefox before 128) prints the value on a small chip of the surface in the chart's text colour instead, which reads on every level. An empty cell prints nothing. | `boolean` | `false` |
| `showLegend` | Show the colour-scale legend under the matrix, from `min` to `max`, with a key for an empty cell while there is one. | `boolean` | `true` |
| `tooltip` | Show a tooltip for the cell under the pointer: its row's name, its column's name and its value, or `noDataLabel` for an empty cell. Pointer only — the data table is where the same numbers are for a keyboard or a screen reader. | `boolean` | `true` |
| `valueFormat` | Writes every number the chart shows: the values in the cells, the legend, the tooltip and the data table. `null` uses `Intl.NumberFormat` in `LOCALE_ID`, compact in the cells from a thousand up. | `((value: number) => string) \| null` | `null` |
| `dataTable` | Render the chart's figures as a table that only assistive technology reaches: a row per row, headed by its name, and a column per column, every value written the way the tooltip writes it, and an empty cell as `noDataLabel`. The cells are one named `role="img"`, which hides everything inside it, so without the table the numbers are nowhere a screen reader can walk. It is rendered beside the matrix, clipped to nothing: it takes no room, draws nothing and holds no tab stop. | `boolean` | `false` |
| `ariaLabel` | Accessible name of the matrix. Its cells carry no text a screen reader reaches, so this is where to say what the chart shows — or turn on `dataTable`. Falls back to `heatmapChart.label`, which is also the data table's caption. | `string \| null` | `null` |
| `noDataLabel` | What an empty cell is called, in the tooltip, the legend's key and the data table — "Closed", "Not measured". Falls back to `heatmapChart.noData`. | `string \| null` | `null` |

## Types

```angular-ts
type WrHeatmapMatrix = readonly (readonly (number | null)[])[];
```

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WrHeatmapMatrix` | The values, rows × columns. A `null`, a value that is not a finite number or one past the end of a shorter row is an empty cell. | `readonly (readonly (number \| null)[])[]` | — |

## CSS variables

Set these on the component's selector or a wrapper, not on `:root`: the defaults are declared on `ngwr/charts/heatmap-chart`'s own element.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-heatmap-chart-active-ring` | `var(--wr-color-on-surface)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-active-ring-width` | `2px` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-cell-height` | `2rem` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-cell-padding-x` | `var(--wr-space-xs)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-cell-radius` | `2px` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-color` | `var(--wr-color-on-surface)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-empty-border` | `var(--wr-color-on-surface-muted)` | `.wr-heatmap-chart` +1 variant override |
| `--wr-heatmap-chart-empty-hatch` | `var(--wr-border-strong)` | `.wr-heatmap-chart` +1 variant override |
| `--wr-heatmap-chart-font-family` | `var(--wr-font-family-base)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-gap` | `2px` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-label-color` | `var(--wr-color-on-surface-muted)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-label-font-size` | `var(--wr-text-xs)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-label-gap` | `var(--wr-space-sm)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-label-line-height` | `var(--wr-text-xs-leading)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-legend-color` | `var(--wr-color-on-surface-muted)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-legend-font-size` | `var(--wr-text-xs)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-legend-gap` | `var(--wr-space-sm)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-legend-margin-top` | `var(--wr-space-md)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-min-share` | `15%` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-row-label-max-width` | `10rem` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-surface` | `var(--wr-color-surface)` | `.wr-heatmap-chart` +1 variant override |
| `--wr-heatmap-chart-swatch-height` | `0.75rem` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-swatch-width` | `1.25rem` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-value-font-size` | `var(--wr-text-xs)` | `.wr-heatmap-chart` |
| `--wr-heatmap-chart-value-font-weight` | `var(--wr-font-weight-medium)` | `.wr-heatmap-chart` |

## Dependencies

| Built on | [Popover](https://ngwr.dev/reference/components/popover) |
| --- | --- |

## See also

- [Calendar Heatmap](https://ngwr.dev/charts/calendar-heatmap) — A year of daily values as a grid of days.
- [Bar Chart](https://ngwr.dev/charts/bar-chart) — Bars for one or several series, grouped or stacked, standing up or lying along a row, with lines over them.
- [Scatter Chart](https://ngwr.dev/charts/scatter-chart) — Points on two numeric axes.
- [Colors](https://ngwr.dev/guides/tokens/colors) — The --wr-color-* tokens, and which one to use for fills, text and borders.
- [Popover](https://ngwr.dev/reference/components/popover) — Show a template, or a text tooltip, next to its trigger.
