# Heatmap chart

> A data table drawn as a grid of cells shaded by value, for patterns across two categories such as days and hours.

## Anatomy

A `<figure class="heatmap-chart">` draws a `<table>` as a grid of shaded cells. Each body row is a row of the grid and each value column is a column, so the grid has the table's shape.

It takes the same markup as a [column chart](column-chart.html). The row headers sit beside the grid and the column headers below it. Cells shade from the lowest value to the highest, and a scale under the grid names both ends. A tooltip shows the cell under the pointer. When the chart has focus, the arrow keys move the tooltip round the grid.

Use a heatmap to find patterns across two categories, such as busy days and hours. To compare exact values, use a [data table](data-table.html).

```
<figure class="heatmap-chart">
  <figcaption>Tasks completed per day, last 12 weeks</figcaption>
  <table>
    <thead>
      <tr>
        <th>Day</th>
        <th data-label="">Week 1</th>
        <th data-label="">Week 2</th>
        <!-- One header per week -->
      </tr>
    </thead>
    <tbody>
      <tr><th>Mon</th><td>2</td><td>5</td><!-- … --></tr>
      <tr><th>Tue</th><td>4</td><td>3</td><!-- … --></tr>
      <!-- One row per day -->
    </tbody>
  </table>
</figure>
```

Set an empty `data-label` to hide a header on the grid, as the week numbers are here. The table keeps the full header for screen readers and the tooltip.

## Values

Add `values` to write each cell's text on it. The text is black or white, whichever reads better on the cell's shade. Give a formatted cell its number in `data-value`.

```
<figure class="heatmap-chart values">
  <figcaption>Revenue by rep, per month</figcaption>
  <table>
    <thead>
      <tr><th>Rep</th><th>Jan</th><th>Feb</th><!-- … --></tr>
    </thead>
    <tbody>
      <tr>
        <th>Priya</th>
        <td data-value="45000">$45K</td>
        <td data-value="58000">$58K</td>
        <!-- … -->
      </tr>
      <!-- One row per rep -->
    </tbody>
  </table>
</figure>
```

## Short labels

A `data-label` on a header cell replaces its text on the grid. A column label runs on over the empty labels after it, so a year of weeks can name each month once. Row labels take up to 40% of the width and end in an ellipsis past it.

For small cells, set `--md-comp-heatmap-chart-row-height` and `--md-comp-heatmap-chart-gap` lower. A wide grid narrows its cells to fit, so show fewer columns on a phone. When the column labels are wider than their columns, the grid keeps every second or third one whole and hides the rest. To choose which ones show, leave labels empty yourself.

```
<figure class="heatmap-chart" style="
    --md-comp-heatmap-chart-row-height: 12px;
    --md-comp-heatmap-chart-gap: 2px;
    --md-comp-heatmap-chart-shape: 2px">
  <figcaption>Tasks completed, last 52 weeks</figcaption>
  <table>
    <thead>
      <tr>
        <th>Day</th>
        <th data-label="Jan">Week 1</th>
        <th data-label="">Week 2</th>
        <!-- One header per week -->
      </tr>
    </thead>
    <tbody>
      <tr><th data-label="">Sun</th><td>0</td><!-- … --></tr>
      <tr><th>Mon</th><td>4</td><!-- … --></tr>
      <!-- One row per day -->
    </tbody>
  </table>
</figure>
```

## Scale

The scale runs from the lowest value to the highest. To fix it, set `data-min` and `data-max` on the chart, such as 0 to 100 for scores. Values past either end take the end's shade. The scale under the grid still runs from the lowest cell to the highest, over only the shades they use.

Cells are at least `--md-comp-heatmap-chart-cell-min-width` wide, 12px by default. A grid with too many columns for its figure scrolls its cells sideways instead of narrowing them, and keeps the row labels in view. The arrow keys scroll the cell they move to into view.

```
<figure class="heatmap-chart values" data-min="0" data-max="100" style="
    --md-comp-heatmap-chart-row-height: 56px;
    --md-comp-heatmap-chart-gap: 4px;
    --md-comp-heatmap-chart-shape: 12px">
  <figcaption>Channel score, paid and organic</figcaption>
  <table>
    <thead>
      <tr><th>Type</th><th>Search</th><th>Social</th><th>Referral</th></tr>
    </thead>
    <tbody>
      <tr><th>Paid</th><td>81</td><td>47</td><td>58</td></tr>
      <tr><th>Organic</th><td>68</td><td>55</td><td>90</td></tr>
    </tbody>
  </table>
</figure>
```

Set `data-mid` to make the scale diverge from that value, such as 0 for a change. The midpoint takes the empty color. Values above it shade towards `--md-comp-heatmap-chart-color` and values below towards `--md-comp-heatmap-chart-low-color`. Both sides run as far as the end further from the midpoint, so equal distances get equal shades.

```
<figure class="heatmap-chart values" data-mid="0">
  <figcaption>Sales change on last year, in percent</figcaption>
  <table>
    <thead>
      <tr><th>Region</th><th>Jan</th><th>Feb</th><!-- … --></tr>
    </thead>
    <tbody>
      <tr><th>North</th><td>-12</td><td>-5</td><!-- … --></tr>
    </tbody>
  </table>
</figure>
```

Set `data-steps` to group the shades into that many bands. The lowest band takes the empty color, and the scale under the grid shows a swatch per band. On a diverging scale each side gets that many bands, and the band nearest the midpoint is the empty color.

```
<figure class="heatmap-chart" data-steps="4">
  <figcaption>Tasks closed per day</figcaption>
  <table>
    <thead>
      <tr><th>Day</th><th>Week 1</th><th>Week 2</th><!-- … --></tr>
    </thead>
    <tbody>
      <tr><th>Mon</th><td>2</td><td>5</td><!-- … --></tr>
    </tbody>
  </table>
</figure>
```

## Chart cards

Put a chart in a card with the figures it explains. The card adds no chart styles, so size the chart with its tokens.

```
<article class="outlined">
  <div class="p-4">
    <dl class="stats my-0">
      <div>
        <dt>Weekly visits</dt>
        <dd>1,002</dd>
        <dd class="stat-change">Busiest on Saturday evening</dd>
      </div>
    </dl>
    <figure class="heatmap-chart">
      <figcaption class="visually-hidden">Visits by day and time, last 7 days</figcaption>
      <table><!-- … --></table>
    </figure>
  </div>
</article>
```

## Values and colors

The chart reads cells the same way a column chart does. A cell's number is its `data-value`, or its text without currency signs, percent signs or grouping commas. Text that is not a single number, such as "n/a" or "$45K" without a `data-value`, is a gap. A gap draws as an outlined cell with no shade, and the tooltip still shows its text. A chart whose cells are all gaps shows its table.

Cells mix `primary` into `surface-container-highest` by their place on the scale. The tooltip swatch takes the cell's shade.

## Accessibility

The table is the chart's text alternative. Give it a header row and a header cell per row, and name the chart with a `<figcaption>`. The chart hides the drawn grid, labels and scale from assistive technology. A `data-label` changes only the drawn label.

The plot is one Tab stop, named by the caption. The arrow keys move one cell the way they point. Home and End jump to the ends of the row, and Escape hides the tooltip. Once the tooltip is hidden, Escape goes to a dialog around the chart. On a touch screen, tap a cell or drag across the grid to show a cell. In a right-to-left page the columns run right to left.

The tooltip is a polite live region, so a screen reader hears each cell's row, column and value as it moves. Shade alone is hard to tell apart, so use `values` when exact numbers matter. In forced colors the cells keep their shades.

## JavaScript

`AutoInit()` starts every `.heatmap-chart`. To start one yourself, call `HeatmapChart.init`. After changing its table, call `update()` to redraw it in place. Or put `data-watch` on the figure, and it redraws itself. It has the line chart's options and methods. `show()` takes a cell, counted row by row from 0.

```
const chart = Expressive.HeatmapChart.init(document.querySelector('.heatmap-chart'), { min: 0, max: 100 });
chart.show(13);  // highlight the second row's second cell in a 12-column grid; -1 hides the tooltip
chart.update();  // redraw from the table; focus and the highlighted row stay
chart.destroy(); // remove the drawing and show the table again
```

## Tokens

Override these on the `.heatmap-chart`.

| Token | Default |
| --- | --- |
| `--md-comp-heatmap-chart-row-height` | 32px |
| `--md-comp-heatmap-chart-cell-min-width` | 12px; below it a wide grid scrolls |
| `--md-comp-heatmap-chart-gap` | 3px between cells |
| `--md-comp-heatmap-chart-shape` | 4px, the cell's corner radius |
| `--md-comp-heatmap-chart-color` | `primary`, the highest value's shade |
| `--md-comp-heatmap-chart-low-color` | `tertiary`, the lowest value's shade on a diverging scale |
| `--md-comp-heatmap-chart-empty-color` | `surface-container-highest`, the lowest value's shade, or the midpoint's |
| `--md-comp-heatmap-chart-grid-color` | `outline-variant`, a gap's outline |

For a selective Sass build, include `"heatmap-chart"` in `$components`.
