Docs Charts Core charts

Heatmap

React example follows below.
ts
import { useState } from "react";
import { Heatmap } from "@grassroot/charts-react";
import { VariantToggle } from "./VariantToggle";
const heatmapMonths = [
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
];
function buildHeatmapDays(): React.ComponentProps<typeof Heatmap>["data"] {
let seed = 20260622;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
};
const days: React.ComponentProps<typeof Heatmap>["data"] = [];
const start = new Date(2025, 5, 22);
for (let index = 0; index < 53 * 7; index += 1) {
const date = new Date(start.getTime() + index * 86400000);
const weekend = date.getDay() === 0 || date.getDay() === 6;
const value = Math.floor(rand() * (weekend ? 5 : 13));
days.push({ date: date.toISOString().slice(0, 10), value: value < 2 ? 0 : value });
}
return days;
}
const heatmapDays = buildHeatmapDays();
const heatmapTotal = heatmapDays.reduce((sum, day) => sum + day.value, 0);
const heatmapSummary =
heatmapTotal.toLocaleString("en-GB") + " production deployments in the last year";
export default function HeatmapDemo() {
const [mark, setMark] = useState<"cell" | "dot">("cell");
return (
<div style={{ width: "100%", minWidth: 0 }}>
<VariantToggle
label="Mark"
options={[
{ value: "cell" as const, label: "Cells" },
{ value: "dot" as const, label: "Punch card" },
]}
value={mark}
onChange={setMark}
/>
<Heatmap
mark={mark}
animation={{ durationMs: 560, staggerMs: 8 }}
data={heatmapDays}
months={heatmapMonths}
total={heatmapSummary}
ariaLabel="Daily production deployment activity over the last 53 weeks"
/>
</div>
);
}
ts
<script setup lang="ts">
import { ref } from "vue";
import { Heatmap } from "@grassroot/charts-vue";
import VariantToggle from "./VariantToggle.vue";
const mark = ref<"cell" | "dot">("cell");
const marks = [
{ value: "cell", label: "Cells" },
{ value: "dot", label: "Punch card" },
];
const heatmapMonths = [
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
];
function buildHeatmapDays(): React.ComponentProps<typeof Heatmap>["data"] {
let seed = 20260622;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
};
const days: React.ComponentProps<typeof Heatmap>["data"] = [];
const start = new Date(2025, 5, 22);
for (let index = 0; index < 53 * 7; index += 1) {
const date = new Date(start.getTime() + index * 86400000);
const weekend = date.getDay() === 0 || date.getDay() === 6;
const value = Math.floor(rand() * (weekend ? 5 : 13));
days.push({ date: date.toISOString().slice(0, 10), value: value < 2 ? 0 : value });
}
return days;
}
const heatmapDays = buildHeatmapDays();
const heatmapTotal = heatmapDays.reduce((sum, day) => sum + day.value, 0);
const heatmapSummary =
heatmapTotal.toLocaleString("en-GB") + " production deployments in the last year";
</script>
<template>
<div style="width: 100%; min-width: 0">
<VariantToggle v-model="mark" label="Mark" :options="marks" />
<Heatmap
:mark="mark"
:animation="{ durationMs: 560, staggerMs: 8 }"
:data="heatmapDays"
:months="heatmapMonths"
:total="heatmapSummary"
aria-label="Daily production deployment activity over the last 53 weeks" />
</div>
</template>
ts
import { ChangeDetectionStrategy, Component, signal } from "@angular/core";
import { GrassrootHeatmap } from "@grassroot/charts-angular";
import type { HeatmapDatum } from "@grassroot/chart-spec";
import { VariantToggleComponent } from "./VariantToggle.component";
@Component({
selector: "app-heatmap-chart-demo",
standalone: true,
imports: [GrassrootHeatmap, VariantToggleComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<app-variant-toggle
label="Mark"
[options]="marks"
[value]="mark()"
(valueChange)="setMark($event)"
/><grassroot-heatmap
[mark]="mark()"
[animation]="{ durationMs: 560, staggerMs: 8 }"
[data]="heatmapDays"
[months]="heatmapMonths"
[total]="heatmapSummary"
ariaLabel="Daily production deployment activity over the last 53 weeks"
/>`,
})
export class HeatmapDemoComponent {
readonly mark = signal<"cell" | "dot">("cell");
readonly marks = [
{ value: "cell", label: "Cells" },
{ value: "dot", label: "Punch card" },
] as const;
readonly heatmapMonths = [
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
];
readonly buildHeatmapDays = () => {
let seed = 20260622;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
};
const days: HeatmapDatum[] = [];
const start = new Date(2025, 5, 22);
for (let index = 0; index < 53 * 7; index += 1) {
const date = new Date(start.getTime() + index * 86400000);
const weekend = date.getDay() === 0 || date.getDay() === 6;
const value = Math.floor(rand() * (weekend ? 5 : 13));
days.push({ date: date.toISOString().slice(0, 10), value: value < 2 ? 0 : value });
}
return days;
};
readonly heatmapDays = this.buildHeatmapDays();
readonly heatmapTotal = this.heatmapDays.reduce((sum, day) => sum + day.value, 0);
readonly heatmapSummary =
this.heatmapTotal.toLocaleString("en-GB") + " production deployments in the last year";
setMark(value: string): void {
if (value === "cell" || value === "dot") this.mark.set(value);
}
}
export default HeatmapDemoComponent;
ts
<script lang="ts">
import { Heatmap } from "@grassroot/charts-svelte";
import VariantToggle from "./VariantToggle.svelte";
let mark = $state<"cell" | "dot">("cell");
const marks = [
{ value: "cell", label: "Cells" },
{ value: "dot", label: "Punch card" },
];
const heatmapMonths = [
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
];
function buildHeatmapDays(): React.ComponentProps<typeof Heatmap>["data"] {
let seed = 20260622;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
};
const days: React.ComponentProps<typeof Heatmap>["data"] = [];
const start = new Date(2025, 5, 22);
for (let index = 0; index < 53 * 7; index += 1) {
const date = new Date(start.getTime() + index * 86400000);
const weekend = date.getDay() === 0 || date.getDay() === 6;
const value = Math.floor(rand() * (weekend ? 5 : 13));
days.push({ date: date.toISOString().slice(0, 10), value: value < 2 ? 0 : value });
}
return days;
}
const heatmapDays = buildHeatmapDays();
const heatmapTotal = heatmapDays.reduce((sum, day) => sum + day.value, 0);
const heatmapSummary =
heatmapTotal.toLocaleString("en-GB") + " production deployments in the last year";
</script>
<div style="width: 100%; min-width: 0">
<VariantToggle
label="Mark"
options={marks}
value={mark}
onchange={(value) => (mark = value as typeof mark)}
/>
<Heatmap
{mark}
animation={{ durationMs: 560, staggerMs: 8 }}
data={heatmapDays}
months={heatmapMonths}
total={heatmapSummary}
ariaLabel="Daily production deployment activity over the last 53 weeks" />
</div>
ts
/** @jsxImportSource solid-js */
import { createSignal } from "solid-js";
import { Heatmap } from "@grassroot/charts-solid";
import type { HeatmapDatum } from "@grassroot/chart-spec";
import { VariantToggle } from "./VariantToggle";
const heatmapMonths = [
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
];
function buildHeatmapDays(): readonly HeatmapDatum[] {
let seed = 20260622;
const rand = () => {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
return seed / 0x7fffffff;
};
const days: HeatmapDatum[] = [];
const start = new Date(2025, 5, 22);
for (let index = 0; index < 53 * 7; index += 1) {
const date = new Date(start.getTime() + index * 86400000);
const weekend = date.getDay() === 0 || date.getDay() === 6;
const value = Math.floor(rand() * (weekend ? 5 : 13));
days.push({ date: date.toISOString().slice(0, 10), value: value < 2 ? 0 : value });
}
return days;
}
const heatmapDays = buildHeatmapDays();
const heatmapTotal = heatmapDays.reduce((sum, day) => sum + day.value, 0);
const heatmapSummary =
heatmapTotal.toLocaleString("en-GB") + " production deployments in the last year";
export default function HeatmapDemo() {
const [mark, setMark] = createSignal<"cell" | "dot">("cell");
const marks = [
{ value: "cell", label: "Cells" },
{ value: "dot", label: "Punch card" },
];
return (
<div style={{ width: "100%", "min-width": "0" }}>
<VariantToggle
label="Mark"
options={marks}
value={mark}
onChange={(value) => setMark(value as "cell" | "dot")}
/>
<Heatmap
mark={mark()}
animation={{ durationMs: 560, staggerMs: 8 }}
data={heatmapDays}
months={heatmapMonths}
total={heatmapSummary}
ariaLabel="Daily production deployment activity over the last 53 weeks" />
</div>
);
}

Installation

API Reference

Import

import { useState } from "react";
import { Heatmap } from "@grassroot/charts-react";
import { VariantToggle } from "./VariantToggle";

Props

Prop
Type
Default
Requirement
Description
data
ChartHeatmapDay[]
required
cell
number | undefined
optional
Cell edge length in px.
gap
number | undefined
optional
Gap between cells in px.
thresholds
number[] | undefined
optional
total
string | undefined
optional
months
string[] | undefined
optional
mark
"cell" | "dot" | undefined
optional
`"cell"` (default): today's bucketed-colour grid.
interactive
boolean | undefined
optional
Gates pointer and keyboard interaction (hit-test, focus-to-first, arrow navigation, Escape) and, with `keyboardNavigation`, whether the plot surface is focusable.
tooltip
boolean | undefined
optional
Renders the active cell's tooltip.
legend
boolean | undefined
optional
Renders the "Less..More" level scale.
activePoint
string | null | undefined
optional
Controlled active cell id — pair with `onActivePointChange`.
defaultActivePoint
string | null | undefined
optional
Uncontrolled initial value for the `activePoint` pair.
onActivePointChange
((id: string | null, datum: ChartHeatmapDay | null) => void) | undefined
optional
speed
number | undefined
optional
Playback-speed multiplier for entrance motion, merged into `animation`'s existing `ChartAnimationConfig.speed` (see `useVibeAnimation` below) — the mechanism this chart already has, not a new one.
Prop
data
Type
ChartHeatmapDay[]
Default
Requirement
required
Description
Prop
cell
Type
number | undefined
Default
Requirement
optional
Description
Cell edge length in px.
Prop
gap
Type
number | undefined
Default
Requirement
optional
Description
Gap between cells in px.
Prop
thresholds
Type
number[] | undefined
Default
Requirement
optional
Description
Prop
total
Type
string | undefined
Default
Requirement
optional
Description
Prop
months
Type
string[] | undefined
Default
Requirement
optional
Description
Prop
mark
Type
"cell" | "dot" | undefined
Default
Requirement
optional
Description
`"cell"` (default): today's bucketed-colour grid.
Prop
interactive
Type
boolean | undefined
Default
Requirement
optional
Description
Gates pointer and keyboard interaction (hit-test, focus-to-first, arrow navigation, Escape) and, with `keyboardNavigation`, whether the plot surface is focusable.
Prop
tooltip
Type
boolean | undefined
Default
Requirement
optional
Description
Renders the active cell's tooltip.
Prop
legend
Type
boolean | undefined
Default
Requirement
optional
Description
Renders the "Less..More" level scale.
Prop
activePoint
Type
string | null | undefined
Default
Requirement
optional
Description
Controlled active cell id — pair with `onActivePointChange`.
Prop
defaultActivePoint
Type
string | null | undefined
Default
Requirement
optional
Description
Uncontrolled initial value for the `activePoint` pair.
Prop
onActivePointChange
Type
((id: string | null, datum: ChartHeatmapDay | null) => void) | undefined
Default
Requirement
optional
Description
Prop
speed
Type
number | undefined
Default
Requirement
optional
Description
Playback-speed multiplier for entrance motion, merged into `animation`'s existing `ChartAnimationConfig.speed` (see `useVibeAnimation` below) — the mechanism this chart already has, not a new one.
Prop
morphDurationMs
Type
number | undefined
Default
Requirement
optional
Description
How long a dataset change tweens, in ms.
Prop
replayToken
Type
string | number | undefined
Default
Requirement
optional
Description
Changing this value replays the entrance animation.
Prop
renderer
Type
ChartRenderer | undefined
Default
Requirement
optional
Description
Prop
className
Type
string | undefined
Default
Requirement
optional
Description
Prop
theme
Type
import("@grassroot/theme").PartialChartTheme | undefined
Default
Requirement
optional
Description
Prop
animation
Type
ChartAnimationInput | undefined
Default
Requirement
optional
Description
Prop
title
Type
string | undefined
Default
Requirement
optional
Description
Prop
description
Type
string | undefined
Default
Requirement
optional
Description
Prop
ariaLabel
Type
string | undefined
Default
Requirement
optional
Description
Prop
keyboardNavigation
Type
boolean | undefined
Default
Requirement
optional
Description

Events

EventPayload
onActivePointChange((id: string | null, datum: ChartHeatmapDay | null) => void) | undefined

Content regions

  • dataSummary

Native attributes

Standard DOM and ARIA attributes not listed above spread onto the root element.

Import

import { ref } from "vue";
import { Heatmap } from "@grassroot/charts-vue";
import VariantToggle from "./VariantToggle.vue";

Props

Prop
Type
Default
Requirement
data
readonly HeatmapDatum[]
required
cell
number | undefined
optional
gap
number | undefined
optional
thresholds
readonly number[] | undefined
optional
mark
"cell" | "dot" | undefined
optional
total
string | undefined
optional
months
readonly string[] | undefined
optional
renderer
ChartRenderer | undefined
"auto"
optional
ariaLabel
string | undefined
optional
className
string | undefined
optional
title
string | undefined
optional
description
string | undefined
optional
theme
PartialChartTheme | undefined
optional
animation
ChartAnimationInput | undefined
optional
Prop
data
Type
readonly HeatmapDatum[]
Default
Requirement
required
Prop
cell
Type
number | undefined
Default
Requirement
optional
Prop
gap
Type
number | undefined
Default
Requirement
optional
Prop
thresholds
Type
readonly number[] | undefined
Default
Requirement
optional
Prop
mark
Type
"cell" | "dot" | undefined
Default
Requirement
optional
Prop
total
Type
string | undefined
Default
Requirement
optional
Prop
months
Type
readonly string[] | undefined
Default
Requirement
optional
Prop
renderer
Type
ChartRenderer | undefined
Default
"auto"
Requirement
optional
Prop
ariaLabel
Type
string | undefined
Default
Requirement
optional
Prop
className
Type
string | undefined
Default
Requirement
optional
Prop
title
Type
string | undefined
Default
Requirement
optional
Prop
description
Type
string | undefined
Default
Requirement
optional
Prop
theme
Type
PartialChartTheme | undefined
Default
Requirement
optional
Prop
animation
Type
ChartAnimationInput | undefined
Default
Requirement
optional
Prop
interactive
Type
boolean | undefined
Default
true
Requirement
optional
Prop
keyboardNavigation
Type
boolean | undefined
Default
true
Requirement
optional
Prop
tooltip
Type
boolean | undefined
Default
true
Requirement
optional
Prop
legend
Type
boolean | undefined
Default
true
Requirement
optional
Prop
activePoint
Type
string | null | undefined
Default
Requirement
optional
Prop
defaultActivePoint
Type
string | null | undefined
Default
null
Requirement
optional
Prop
speed
Type
number | undefined
Default
Requirement
optional
Prop
morphDurationMs
Type
number | undefined
Default
Requirement
optional
Prop
replayToken
Type
string | number | undefined
Default
Requirement
optional

Events

EventPayload
update:activePoint[id: string | null]
activePointChange[id: string | null, datum: HeatmapDatum | null]

Content regions

  • dataSummary

Import

import { ChangeDetectionStrategy, Component, signal } from "@angular/core";
import { GrassrootHeatmap } from "@grassroot/charts-angular";
import type { HeatmapDatum } from "@grassroot/chart-spec";
import { VariantToggleComponent } from "./VariantToggle.component";

Props

Prop
Type
Default
Requirement
data
readonly HeatmapDatum[]
required
cell
number
optional
gap
number
optional
thresholds
readonly number[]
optional
total
string
optional
months
readonly string[]
optional
renderer
ChartRenderer
"auto"
optional
speed
number
optional
morphDurationMs
number
optional
replayToken
string | number
optional
ariaLabel
string
optional
className
string
optional
title
string
optional
description
string
optional
Prop
data
Type
readonly HeatmapDatum[]
Default
Requirement
required
Prop
cell
Type
number
Default
Requirement
optional
Prop
gap
Type
number
Default
Requirement
optional
Prop
thresholds
Type
readonly number[]
Default
Requirement
optional
Prop
total
Type
string
Default
Requirement
optional
Prop
months
Type
readonly string[]
Default
Requirement
optional
Prop
renderer
Type
ChartRenderer
Default
"auto"
Requirement
optional
Prop
speed
Type
number
Default
Requirement
optional
Prop
morphDurationMs
Type
number
Default
Requirement
optional
Prop
replayToken
Type
string | number
Default
Requirement
optional
Prop
ariaLabel
Type
string
Default
Requirement
optional
Prop
className
Type
string
Default
Requirement
optional
Prop
title
Type
string
Default
Requirement
optional
Prop
description
Type
string
Default
Requirement
optional
Prop
theme
Type
PartialChartTheme
Default
Requirement
optional
Prop
animation
Type
ChartAnimationInput
Default
Requirement
optional
Prop
interactive
Type
boolean
Default
true
Requirement
optional
Prop
keyboardNavigation
Type
boolean
Default
true
Requirement
optional
Prop
tooltip
Type
boolean
Default
true
Requirement
optional
Prop
legend
Type
boolean
Default
true
Requirement
optional
Prop
mark
Type
HeatmapOptions["mark"]
Default
Requirement
optional
Prop
activePoint
Type
string | null
Default
Requirement
optional
Prop
defaultActivePoint
Type
string | null
Default
null
Requirement
optional

Events

EventPayload
activePointChange[string | null, HeatmapDatum | null]

Content regions

  • dataSummary

Import

import { Heatmap } from "@grassroot/charts-svelte";
import VariantToggle from "./VariantToggle.svelte";

Props

Prop
Type
Default
Requirement
data
readonly HeatmapDatum[]
required
cell
number
optional
gap
number
optional
thresholds
readonly number[]
optional
mark
HeatmapOptions["mark"]
optional
total
string
optional
months
readonly string[]
optional
renderer
ChartRenderer
"auto"
optional
ariaLabel
string
optional
class
string
optional
title
string
optional
description
string
optional
theme
PartialChartTheme
optional
animation
ChartAnimationInput
optional
Prop
data
Type
readonly HeatmapDatum[]
Default
Requirement
required
Prop
cell
Type
number
Default
Requirement
optional
Prop
gap
Type
number
Default
Requirement
optional
Prop
thresholds
Type
readonly number[]
Default
Requirement
optional
Prop
mark
Type
HeatmapOptions["mark"]
Default
Requirement
optional
Prop
total
Type
string
Default
Requirement
optional
Prop
months
Type
readonly string[]
Default
Requirement
optional
Prop
renderer
Type
ChartRenderer
Default
"auto"
Requirement
optional
Prop
ariaLabel
Type
string
Default
Requirement
optional
Prop
class
Type
string
Default
Requirement
optional
Prop
title
Type
string
Default
Requirement
optional
Prop
description
Type
string
Default
Requirement
optional
Prop
theme
Type
PartialChartTheme
Default
Requirement
optional
Prop
animation
Type
ChartAnimationInput
Default
Requirement
optional
Prop
interactive
Type
boolean
Default
true
Requirement
optional
Prop
keyboardNavigation
Type
boolean
Default
true
Requirement
optional
Prop
tooltip
Type
boolean
Default
true
Requirement
optional
Prop
legend
Type
boolean
Default
true
Requirement
optional
Prop
activePoint
Type
string | null
Default
Requirement
optional
Prop
defaultActivePoint
Type
string | null
Default
Requirement
optional
Prop
onActivePointChange
Type
(id: string | null, datum: HeatmapDatum | null) => void
Default
Requirement
optional
Prop
speed
Type
number
Default
Requirement
optional
Prop
morphDurationMs
Type
number
Default
Requirement
optional
Prop
replayToken
Type
string | number
Default
Requirement
optional

Events

EventPayload
onActivePointChange(id: string | null, datum: HeatmapDatum | null) => void

Content regions

  • dataSummary

Import

import { createSignal } from "solid-js";
import { Heatmap } from "@grassroot/charts-solid";
import type { HeatmapDatum } from "@grassroot/chart-spec";
import { VariantToggle } from "./VariantToggle";

Props

Prop
Type
Default
Requirement
data
readonly HeatmapDatum[]
required
cell
number
13
optional
gap
number
3
optional
mark
HeatmapOptions["mark"]
optional
thresholds
readonly number[]
optional
total
string
"Activity heatmap"
optional
months
readonly string[]
optional
renderer
ChartRenderer
"auto"
optional
ariaLabel
string
optional
title
string
optional
description
string
optional
theme
PartialChartTheme
optional
animation
ChartAnimationInput
optional
speed
number
optional
Prop
data
Type
readonly HeatmapDatum[]
Default
Requirement
required
Prop
cell
Type
number
Default
13
Requirement
optional
Prop
gap
Type
number
Default
3
Requirement
optional
Prop
mark
Type
HeatmapOptions["mark"]
Default
Requirement
optional
Prop
thresholds
Type
readonly number[]
Default
Requirement
optional
Prop
total
Type
string
Default
"Activity heatmap"
Requirement
optional
Prop
months
Type
readonly string[]
Default
Requirement
optional
Prop
renderer
Type
ChartRenderer
Default
"auto"
Requirement
optional
Prop
ariaLabel
Type
string
Default
Requirement
optional
Prop
title
Type
string
Default
Requirement
optional
Prop
description
Type
string
Default
Requirement
optional
Prop
theme
Type
PartialChartTheme
Default
Requirement
optional
Prop
animation
Type
ChartAnimationInput
Default
Requirement
optional
Prop
speed
Type
number
Default
Requirement
optional
Prop
morphDurationMs
Type
number
Default
Requirement
optional
Prop
replayToken
Type
string | number
Default
Requirement
optional
Prop
interactive
Type
boolean
Default
true
Requirement
optional
Prop
keyboardNavigation
Type
boolean
Default
true
Requirement
optional
Prop
tooltip
Type
boolean
Default
true
Requirement
optional
Prop
legend
Type
boolean
Default
true
Requirement
optional
Prop
activePoint
Type
string | null
Default
Requirement
optional
Prop
defaultActivePoint
Type
string | null
Default
null
Requirement
optional
Prop
onActivePointChange
Type
(id: string | null, datum: HeatmapDatum | null) => void
Default
Requirement
optional
Prop
class
Type
string
Default
Requirement
optional

Events

EventPayload
onActivePointChange(id: string | null, datum: HeatmapDatum | null) => void

Content regions

  • dataSummary