Charts

Heatmap

A matrix heatmap — two categorical axes, intensity for the value.

Server safePure SVG0 dependenciesSource

Import

Charts live at their own subpath with their own stylesheet, so an app that never draws one pays nothing for them.

import { Heatmap } from '@the_viveksingh/vivek-ui/charts'
import '@the_viveksingh/vivek-ui/charts.css'

Two categorical axes, intensity for the value

The GitHub-contribution shape, generalised. Intensity is a lightness ramp mixed from the tokens with color-mix, so it holds in dark mode and survives every form of colour blindness - and the real numbers ship in the accessible table either way.

<Heatmap
  title="Errors by service and day"
  showValues
  columns={['Mon', 'Tue', 'Wed', 'Thu', 'Fri']}
  data={[
    { x: 'Mon', y: 'API', value: 12 },
    { x: 'Tue', y: 'API', value: 41 },
    { x: 'Mon', y: 'Web', value: 25 },
    // ...one cell per (column, row) pair; gaps render as empty cells
  ]}
/>

Accessibility

A chart is not an image

Every chart carries role="img" with a generated accessible name, and renders a real <table> alongside it — visually hidden, never display: none — so a screen-reader user gets the actual numbers instead of "chart". Set accessibleTable={false} only if you have provided the data in a table elsewhere on the page.

No series is ever encoded by colour alone: each carries a distinct dash pattern and marker shape on top of its colour, so the chart survives greyscale printing and every common form of colour blindness.

The palette is verified rather than asserted. Each theme has its own six colours, because one set cannot clear the 3:1 non-text contrast threshold against both a white and a near-black plot surface. Separation is measured under simulated protanopia, deuteranopia and tritanopia: for one to four series — which is nearly every chart — the closest pair sits at ΔE 17, slightly better than the unmodified Okabe-Ito palette it is derived from. Past four series the dash patterns and marker shapes carry the distinction.

Hostile data

Real series contain gaps and rubbish. Every chart is tested against empty arrays, a single point, all-equal values, negatives crossing zero, NaN, ±Infinity, 1e308 and 1e-320, with an assertion that no rendered SVG attribute ever contains NaN. A bad row degrades the chart; it does not blank your page.


Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for Heatmap
PropTypeDefaultDescription
data requiredreadonly HeatmapCell[]——
columnsreadonly string[]—Explicit column order. Also renders columns that have no data yet.
rowsreadonly string[]—Explicit row order. Also renders rows that have no data yet.
minnumber—Intensity scale bounds. Default to the data's own extent.
maxnumber——
colorstring—Base colour of the ramp. Any CSS colour; defaults to the first chart token. The ramp runs from the surface colour to this, so it holds in both themes without a second palette — intensity is mixed in the browser with `color-mix`.
cellSizenumber—Height of one row in px. Cells are square-ish; width divides evenly.
showValuesboolean—Print each value inside its cell. Off above 5% of cells being 4+ characters wide.