Components / Charts
ActivityHeatmap
See a year of activity at a glance, one square per day.
Install
npx shadcn@latest add https://irisui.dev/r/activity-heatmap.jsonimport { ActivityHeatmap } from "@/components/iris/activity-heatmap/activity-heatmap";Preview
ActivityHeatmap
A contribution-style calendar with one square per day, weeks as columns and four accent tints for how busy each day was.
When to use
- A year (or a few months) of daily counts where rhythm, streaks and quiet stretches matter more than exact comparisons: deploys, commits, sessions, messages.
- With a range switch: put it in
actionsand swap thedaysdata; the grid recolours in a sweep. - Not when the exact comparison between days is the point; use
BarChart.
Usage
import { ActivityHeatmap } from "@/components/iris/activity-heatmap/activity-heatmap";
function DocumentChanges({ days }) {
const [selected, setSelected] = React.useState(null);
return h(ActivityHeatmap, {
days, // [{ date: '2026-10-21', count: 7 }, ...] oldest first
label: "Document changes in notdocs, last 12 months",
period: "the last year",
unit: { one: "change", other: "changes" },
weekStartsOn: 1,
selectedDate: selected,
onSelectDate: setSelected,
});
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
days |
{ date: string; count: number }[] |
required | One entry per day as YYYY-MM-DD, oldest first. Missing days inside the range count as zero. |
label |
string |
required | Accessible name for the grid. |
period |
string |
required | Finishes the summary line: "1,038 changes in {period}". |
unit |
{ one: string; other: string } |
contribution / contributions | Nouns for the count. |
thresholds |
[number, number, number] |
quarters of the busiest day | Upper bounds for levels one to three; anything above is level four. |
weekStartsOn |
0 | 1 |
0 |
Sunday or Monday as the first row. |
selectedDate |
string | null |
— | Highlighted day. |
onSelectDate |
(date: string) => void |
— | Called on click, Enter or Space. |
actions |
ReactNode |
— | Controls beside the summary, such as a range switch. |
locale |
string |
"en-US" |
Formatting locale; fixed by default so server and client match. |
className |
string |
— | Class on the root. |
Notes
- Keyboard: arrow keys move by day and week, Home/End go to the range edges, Enter/Space selects, Escape dismisses the tooltip.
- Accessibility: cells are labelled, a hidden legend explains the levels, legend swatches toggle to highlight one level, and totals and the hovered day are announced. The tooltip itself is hidden from assistive technology.
- Motion: cells wave in once on view; the tooltip glides between cells; a new range recolours in a sweep. Reduced motion skips these.
- Tints are mixed from
--accentover--ink/--surface, so it follows the theme. A full year needs roughly 700px; in narrower containers the grid scrolls sideways (the tooltip stays pinned to its cell) and the summary switches to a short caption below 440px.