Activity Heatmap
Activity by day in GitHub's calendar grid, with quantile shading, a keyboard grid and hover detail.
Rendering Activity Heatmap…
Installation
Copy the source into your project with the CLI. It lands under @/components/ui and is yours to edit.
npx mlola-ui add activity-heatmapUsage
import { ActivityHeatmap } from "@/components/ui/activity-heatmap";
// Counts by day; days not listed count as zero.
const data = [
{ date: "2026-09-21", count: 3 },
{ date: "2026-09-22", count: 7 },
{ date: "2026-09-24", count: 1 },
{ date: "2026-09-25", count: 12 },
];
export default function Example() {
return <ActivityHeatmap data={data} end="2026-09-26" weeks={8} unit="commits" label="Commits in the last eight weeks" />;
}Styling comes from the engine stylesheet and the nearest theme. Set it once at the root:
<html data-theme="graphite" data-mode="light">HTML
The markup the example above renders, exactly. It needs no script: write it from any language or template.
<div class="ml-heatmap" data-tone="primary" style="--ml-heatmap-weeks:8">
<div class="ml-heatmap-grid" role="grid" aria-label="Commits in the last eight weeks" aria-readonly="true">
<div class="ml-heatmap-months" aria-hidden="true">
<span class="ml-heatmap-month" style="grid-column:2">Aug</span>
<span class="ml-heatmap-month" style="grid-column:7">Sep</span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:2"><span>Mon</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, August 3, 2026" tabindex="-1" style="grid-column:2;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, August 10, 2026" tabindex="-1" style="grid-column:3;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, August 17, 2026" tabindex="-1" style="grid-column:4;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, August 24, 2026" tabindex="-1" style="grid-column:5;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, August 31, 2026" tabindex="-1" style="grid-column:6;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, September 7, 2026" tabindex="-1" style="grid-column:7;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Monday, September 14, 2026" tabindex="-1" style="grid-column:8;grid-row:2"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="1" aria-label="3 commits on Monday, September 21, 2026" tabindex="-1" style="grid-column:9;grid-row:2"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:3"><span aria-hidden="true" data-hidden="true">Tue</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, August 4, 2026" tabindex="-1" style="grid-column:2;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, August 11, 2026" tabindex="-1" style="grid-column:3;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, August 18, 2026" tabindex="-1" style="grid-column:4;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, August 25, 2026" tabindex="-1" style="grid-column:5;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, September 1, 2026" tabindex="-1" style="grid-column:6;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, September 8, 2026" tabindex="-1" style="grid-column:7;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Tuesday, September 15, 2026" tabindex="-1" style="grid-column:8;grid-row:3"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="2" aria-label="7 commits on Tuesday, September 22, 2026" tabindex="-1" style="grid-column:9;grid-row:3"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:4"><span>Wed</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, August 5, 2026" tabindex="-1" style="grid-column:2;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, August 12, 2026" tabindex="-1" style="grid-column:3;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, August 19, 2026" tabindex="-1" style="grid-column:4;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, August 26, 2026" tabindex="-1" style="grid-column:5;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, September 2, 2026" tabindex="-1" style="grid-column:6;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, September 9, 2026" tabindex="-1" style="grid-column:7;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, September 16, 2026" tabindex="-1" style="grid-column:8;grid-row:4"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Wednesday, September 23, 2026" tabindex="-1" style="grid-column:9;grid-row:4"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:5"><span aria-hidden="true" data-hidden="true">Thu</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, August 6, 2026" tabindex="-1" style="grid-column:2;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, August 13, 2026" tabindex="-1" style="grid-column:3;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, August 20, 2026" tabindex="-1" style="grid-column:4;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, August 27, 2026" tabindex="-1" style="grid-column:5;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, September 3, 2026" tabindex="-1" style="grid-column:6;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, September 10, 2026" tabindex="-1" style="grid-column:7;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Thursday, September 17, 2026" tabindex="-1" style="grid-column:8;grid-row:5"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="1" aria-label="1 commits on Thursday, September 24, 2026" tabindex="-1" style="grid-column:9;grid-row:5"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:6"><span>Fri</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, August 7, 2026" tabindex="-1" style="grid-column:2;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, August 14, 2026" tabindex="-1" style="grid-column:3;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, August 21, 2026" tabindex="-1" style="grid-column:4;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, August 28, 2026" tabindex="-1" style="grid-column:5;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, September 4, 2026" tabindex="-1" style="grid-column:6;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, September 11, 2026" tabindex="-1" style="grid-column:7;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Friday, September 18, 2026" tabindex="-1" style="grid-column:8;grid-row:6"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="3" aria-label="12 commits on Friday, September 25, 2026" tabindex="-1" style="grid-column:9;grid-row:6"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:7"><span aria-hidden="true" data-hidden="true">Sat</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, August 8, 2026" tabindex="-1" style="grid-column:2;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, August 15, 2026" tabindex="-1" style="grid-column:3;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, August 22, 2026" tabindex="-1" style="grid-column:4;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, August 29, 2026" tabindex="-1" style="grid-column:5;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, September 5, 2026" tabindex="-1" style="grid-column:6;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, September 12, 2026" tabindex="-1" style="grid-column:7;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, September 19, 2026" tabindex="-1" style="grid-column:8;grid-row:7"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Saturday, September 26, 2026" tabindex="0" style="grid-column:9;grid-row:7"></span>
</div>
<div class="ml-heatmap-row" role="row">
<span class="ml-heatmap-weekday" role="rowheader" style="grid-row:8"><span aria-hidden="true" data-hidden="true">Sun</span></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, August 9, 2026" tabindex="-1" style="grid-column:2;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, August 16, 2026" tabindex="-1" style="grid-column:3;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, August 23, 2026" tabindex="-1" style="grid-column:4;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, August 30, 2026" tabindex="-1" style="grid-column:5;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, September 6, 2026" tabindex="-1" style="grid-column:6;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, September 13, 2026" tabindex="-1" style="grid-column:7;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" aria-label="0 commits on Sunday, September 20, 2026" tabindex="-1" style="grid-column:8;grid-row:8"></span>
<span class="ml-heatmap-cell" role="gridcell" data-level="0" data-outside="true" aria-hidden="true" style="grid-column:9;grid-row:8"></span>
</div>
</div>
<div class="ml-heatmap-footer">
<span class="ml-heatmap-caption">23 commits over 4 active days</span>
<span class="ml-heatmap-legend" aria-hidden="true">Less<span class="ml-heatmap-cell" data-level="0"></span><span class="ml-heatmap-cell" data-level="1"></span><span class="ml-heatmap-cell" data-level="2"></span><span class="ml-heatmap-cell" data-level="3"></span><span class="ml-heatmap-cell" data-level="4"></span>More</span>
</div>
</div>Options
| Prop | Values | Default |
|---|---|---|
tone | primaryinfosuccesswarning | primary |
Every prop with a fixed set of values, read from the exported TypeScript types with its doc comment. Open the Code tab above for the rest of the API.
Classes and attributes
A class names each element; data-* and aria-* attributes carry its variant and state. The values listed are the ones the stylesheet draws; the default needs no attribute.
| Class | Attributes |
|---|---|
ml-heatmap | data-tone="info | success | warning" |
ml-heatmap-grid | — |
ml-heatmap-months | — |
ml-heatmap-row | — |
ml-heatmap-month | — |
ml-heatmap-weekday | — |
ml-heatmap-cell | data-activedata-level="1 | 2 | 3 | 4"data-outside |
ml-heatmap-tooltip | — |
ml-heatmap-footer | — |
ml-heatmap-legend | — |
ml-heatmap-caption | — |
Accessibility and theming
- Semantic elements, accessible names and state relationships are part of the contract.
- Keyboard, pointer and touch paths are operable; focus stays visible in every theme.
- Light and dark modes pass WCAG AA contrast in all 5 canonical themes.
- Reduced motion and forced colors keep meaning and operation.
Dependencies
Installed alongside this component. No third-party UI runtime is ever added.
@mlola-ui/behavior