Skip to the page
TACTILE UI

Kit part · Charts

Lamp heatmap

A heatmap as a matrix of small panel lamps set into the plate: each lamp’s brightness is the value, off to full in four steps, with a printed legend. Deploys per day over a year, incidents by hour of the week, energy by the hour.

Element
<tui-heatmap>
Needs
kit.css + kit.mjs
Rules
heatmap.rules.md · 5
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

Energy by the hour · kWh, amber
A quarter · the newest week last
A rate · summary="mean", pass rate by suite
A grid of your own · regions × services
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartOff to fullHover · readFocusNo reading
Heatmap

Graphite

PartOff to fullHover · readFocusNo reading
Heatmap
03

API

NameKindValuesWhat it does
<tui-heatmap>element—The matrix: builds the head with its display, the grid of lamps and the legend inside itself.
layoutattribute"calendar" · "week" · "grid"Days (weekdays down, weeks across, from start), the hours of a week (168 values, Monday first), or rows × cols of your own.
startattributeISO dateCalendar: the day of the first value.
rows · cols · row-labels · col-labelsattributenumbers · comma listsGrid: the size, and the names printed down the side and across the top.
valuesattributenumbers, comma-separatedThe values from markup, in the layout’s order; an empty place or a dash is no reading.
label · unit · unit-oneattributetextThe chart’s name; the unit read with every value (“6 deploys”, and “1 deploy” with unit-one).
min · maxattribute0 · the largest valueThe range cut into four equal steps. At min or below a lamp is off.
thresholdsattributefour numbers, e.g. "1,3,6,10"The lower bound of each step instead; the legend prints them.
precision · locale · first-dayattributeauto · the page’s · the locale’sDecimals of a value; the language of dates; the weekday in the top row (0 Sunday … 6).
summaryattribute"total" · "mean"How the values are summed up. Counts add up (the default: “62 deploys over 91 days”); rates and shares (a pass rate, retention) are averaged, with the highest and lowest cells named, and at rest the display shows the mean.
data-lampattributeamber · green · red · blue · whiteThe lamps’ colour, one per chart: green for things done, red for failures, blue for traffic that only informs, white for plain counts, amber otherwise.
.setData(values, { start })methodarray or rows · ISO dateNew values; with the same layout the lamps fade to their new brightness.
.push(value)methodnumber or nullOne more value at the end: today’s count, the hour just finished. A calendar grows a week when it must.
.values · .focusValue(i)property · methodarray · indexThe values given; move the tab stop (and the focus) to a value’s lamp.
pickeventdetail { index, value, label, date }A lamp was chosen: click, Enter or Space.
Arrowskeyboardone lampAcross and down the grid; blank sockets are skipped. In a calendar, down is the next day and right the next week.
Home · Endkeyboardrow · with Ctrl, the gridThe first or last lamp in the row, or in the whole grid.
PageUp · PageDownkeyboardfour columnsBack or on four columns: about a month in a calendar, four hours in a week.
Enter · SpacekeyboardpickFires pick for the focused lamp.
--tui-heatmap-cellCSS property8–20px, from the widthA lamp’s diameter. Set it to fix the size; wider than its box, the grid scrolls inside itself.
.is-hover · .is-focusclasson <tui-heatmap>Forced states, for documentation and specimens only: the newest lamp is held read, or focused.
04

Markup

<tui-heatmap label="Deploys per day · api" layout="calendar"
    start="2025-09-29" unit="deploys" unit-one="deploy" data-lamp="green"
    values="3,7,5,9,4,0,0,…"></tui-heatmap>

<tui-heatmap label="Incidents by hour" layout="week" data-lamp="red"
    unit="incidents" unit-one="incident" values="…168 values, Monday 00:00 first…"></tui-heatmap>
import '/kit/kit.mjs';   // defines <tui-heatmap>

const map = document.querySelector('tui-heatmap');

// A value a day from start, oldest first; null for a day with no reading.
map.setData(deploysPerDay, { start: '2025-09-29' });
map.push(todaysDeploys);   // one more day at the end

// A lamp chosen by click, Enter or Space.
map.addEventListener('pick', (e) => showDeploys(e.detail.date, e.detail.value));

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • A WAI-ARIA grid: rows of gridcell lamps, each named in full (“Tue 14 Oct 2025: 6 deploys”), with one tab stop that the arrows, Home/End and PageUp/PageDown move. The row headers name the weekdays.
  • The grid is described by a summary that states the data: the total, the busiest day, how many had none, and what each step of light means. With summary="mean" a rate is averaged instead, and its highest and lowest cells are named.
  • Hover and focus read the same lamp on the display in the head; the lamp being read is ringed in print as well as lit, so nothing depends on seeing brightness alone. The legend prints each step’s range.
  • Each step of light is clearly apart from the next, and the glow grows with it; a day with no reading is an empty socket with no lens at all.
  • Reduced motion: lamps change brightness at once and no frame is asked for; nothing else moves.
06

How it’s built

Two layers. The sockets are spans in rows, so the ARIA grid is real and the columns line up: each is a hole in the plate (a dark ring, the upper wall’s shadow falling on the glass, a lit lower lip) with the lens’s reflection of the room’s lamp, the same on every lamp, so it never moves with the brightness. The sockets carry the focus and hover rings and never restyle when the data changes. Behind them is one canvas: every unlit lens (dark glass tinted by the lamp colour) is baked once per layout, and the lit lamp (a warm-white core in the lamp colour, darker at the rim, inside its glow) is one sprite, drawn at the step’s brightness. A frame is one blit per lit lamp; new values fade 140 ms up and 260 ms down, and the loop stops when every lamp has arrived. The size comes from the width by a container query, 8 to 20 px, read back in layout units so a scaled thumbnail lines up exactly.

07

Rules

heatmap.rules.mdIn the brain

One lamp colour per matrix, chosen for what the count means; the legend says what each step of light is worth.

5 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Lamp

The only colour in the kit: a lamp that is on. Green is on, amber is waiting, red is recording or failing, blue is linked, white is standing by. Always next to words.

September2026
14151617181920

Calendar

A month on a plate. The month and year sit on two drums behind a window and roll when you page; the chosen day is a latched cap, a range is a groove cut between two caps, and events are lamps named in the line below.

Reel 342:10
Mixed0:30

Table

Rows of data with printed column labels, figures right-aligned in columns, and status in lamps with words.

Docs
Finish
Volume