Activity Heatmap
GitHub-style contribution calendar: a year of day cells, one column per week, each tinted by intensity on a five-step accent ramp, with hover and keyboard detail.
<arc-activity-heatmap> Overview
>
Activity Heatmap is the contribution calendar every developer already knows how to read: fifty-two columns of seven cells, one cell per day, tinted deeper as the day gets busier. Month labels run along the top, sparse weekday labels down the side, and a Less→More legend anchors the ramp. Hovering a cell (or focusing the grid and pressing the arrow keys) raises it with the house glow and shows its detail — the date and the day's label or value — in a small built-in bubble.
The `data` property takes one entry per day with activity: an ISO `date`, a numeric `value`, and an optional `label` that replaces the bare number in the hover detail ("7 commits" reads better than "7"). Days without an entry render as empty cells, so sparse data needs no zero-filling. By default the grid ends today and spans back `weeks` columns; pin `end-date` to show a fixed window. When `end-date` is unset and the component renders server-side, the anchor is derived from the newest date in the data instead of the server's clock, so the server output stays a pure function of props — pin `end-date` whenever server and client must agree exactly.
Intensity is relative by default: the nonzero values are split into quartiles, and each quartile maps to one step of the accent ramp, so a quiet repository and a busy one both use the full range. When absolute comparison matters — two heatmaps side by side, or a known ceiling — set `max` and the ramp becomes a linear scale from zero to that value. The ramp itself is composed from `--accent-primary-rgb` at five alphas, so overriding one token recolors the whole calendar.
To assistive technology the grid is a single image with a computed description — day count, end date, total, and active days — rather than three hundred and sixty-four tab stops. Keyboard users still get per-day detail: the grid takes focus once, up and down move a day, left and right move a week, Home and End jump to the ends, and a live region announces each cell.Guidelines
When to use
- Use it for daily event counts over months — commits, deploys, workouts, practice sessions
- Give each entry a label ("7 commits") — it is what the hover detail and screen reader announcement read out
- Pin end-date when the window is a fixed report ("2025 in review") or when server and client must render identically
- Set max when readers will compare two heatmaps side by side — quartile ramps are relative and would mislead
- Match week-start to your audience: Sunday is the GitHub convention, Monday the ISO one
- Keep the legend unless the surrounding UI already explains the ramp — it is the only key the colors have
When not to use
- Do not use it for a continuous metric like latency or revenue — that trend is Sparkline's job; the heatmap shows daily density
- Do not use it for per-period pass/fail health — Uptime owns discrete status history with its own color semantics
- Do not use it to display scheduled items on dates — Event Calendar shows what happens when; the heatmap shows how much happened
- Do not encode more than one measure per cell — one value, one ramp; two measures need two heatmaps
Features
- One cell per day in week columns, the layout every contribution graph has taught readers
- Five-step intensity ramp composed from `--accent-primary-rgb` — one token override recolors it all
- Quartile mapping by default, so sparse and busy datasets both use the full range
- Linear scale via `max` when absolute comparison across heatmaps matters
- Sparse `data` is fine: days without an entry render as empty cells, no zero-filling
- Month labels along the top and Mon/Wed/Fri weekday labels down the side, from `Intl`
- Hover or arrow-key a cell to raise it with the house glow and show its detail bubble
- Grid-semantics keyboard inspection: up/down a day, left/right a week, Home/End the span
- Single tab stop with a rich `aria-label` summary and a live region for keyboard users
- Sunday or Monday week start via `week-start`; deterministic server rendering documented on `end-date`
- Cell size and gap tunable via `--activity-heatmap-cell` and `--activity-heatmap-gap`
Preview
Usage
Layout and styling work without JavaScript via the HTML/CSS versions. Interactive features like events and state management require the Web Component or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<arc-activity-heatmap id="api-activity" end-date="2026-12-31" weeks="52"></arc-activity-heatmap>
<script>
const map = document.querySelector('#api-activity');
// One entry per day with activity; empty days need no entry.
map.data = [
{ date: '2026-12-29', value: 3, label: '3 commits' },
{ date: '2026-12-30', value: 11, label: '11 commits' },
{ date: '2026-12-31', value: 6, label: '6 commits' },
];
// Quartiles scale the ramp by default; pin max for an absolute scale:
map.max = 12;
</script> import { ActivityHeatmap } from '@arclux/arc-ui-react';
const year = commits.map((day) => ({
date: day.date, // 'YYYY-MM-DD'
value: day.count,
label: `${day.count} commits`,
}));
export function ContributionGraph() {
return <ActivityHeatmap data={year} weeks={52} weekStart="monday" />;
} <script setup>
import { ActivityHeatmap } from '@arclux/arc-ui-vue';
const year = commits.map((day) => ({
date: day.date,
value: day.count,
label: `${day.count} commits`,
}));
</script>
<template>
<ActivityHeatmap :data="year" :weeks="52" week-start="monday" />
</template> <script>
import { ActivityHeatmap } from '@arclux/arc-ui-svelte';
const year = commits.map((day) => ({
date: day.date,
value: day.count,
label: `${day.count} commits`,
}));
</script>
<ActivityHeatmap data={year} weeks={52} weekStart="monday" /> import { Component } from '@angular/core';
import { ActivityHeatmap } from '@arclux/arc-ui-angular';
@Component({
imports: [ActivityHeatmap],
template: `
<arc-activity-heatmap [data]="year" weeks="52" week-start="monday" />
`,
})
export class ContributionGraphComponent {
year = commits.map((day) => ({
date: day.date,
value: day.count,
label: `${day.count} commits`,
}));
} import { ActivityHeatmap } from '@arclux/arc-ui-solid';
const year = commits.map((day) => ({
date: day.date,
value: day.count,
label: `${day.count} commits`,
}));
export function ContributionGraph() {
return <ActivityHeatmap data={year} weeks={52} weekStart="monday" />;
} import { ActivityHeatmap } from '@arclux/arc-ui-preact';
const year = commits.map((day) => ({
date: day.date,
value: day.count,
label: `${day.count} commits`,
}));
export function ContributionGraph() {
return <ActivityHeatmap data={year} weeks={52} weekStart="monday" />;
} API
-
dataArray<{date: string, value: number, label?: string}>[] - One entry per day with activity: an ISO `date` (YYYY-MM-DD), a numeric `value` mapped to the intensity ramp, and an optional `label` shown in the hover detail in place of the bare value (e.g. "7 commits"). Days in the rendered span with no entry render as empty cells, so sparse data is fine. Property only — set it from script or a framework binding.
-
end-datestring'' - The newest day shown, as an ISO string (YYYY-MM-DD). Unset: today in the browser; on the server, the newest date in `data` (see above).
-
weeksnumber52 - How many week columns to render, counting back from the week containing the end date (default 52). The last column is truncated after the end date, so the newest cell is always the end date itself.
-
week-startstring'sunday' - Which day starts each column: "sunday" (default, the GitHub convention) or "monday".
-
maxnumbernull - When set, intensity is a linear scale from 0 to this value instead of the default quartile mapping: each nonzero value lands on step 1-4 by `value / max`. Values at or above `max` render as step 4.
-
legendbooleantrue - Whether to render the Less→More swatch strip under the grid (default true; set the attribute to the string "false" to disable from markup).
See Also
- Uptime Status-page uptime history strip: one slim tick per period, colored by status, with hover and keyboard detail. Accepts plain uptime fractions or explicit status objects and renders the computed overall percentage above the track.
- Sparkline Tiny inline SVG chart for embedding lightweight line or bar visualizations inside tables, stat cards, and dashboards. Renders from a simple comma-separated data string with no external charting dependencies.
- Event Calendar Scheduling calendar with month and week views that renders all-day and multi-day event chips colored by the chart palette.
- Chart An SVG chart component for dashboards with line, area, bar, and donut types. Data-driven from a series array, with automatic nice-tick scales, a legend, hover crosshair and tooltips, and a visually-hidden data table for assistive technology.