HAHS.
Back to Catalog

Calendar Heatmap

chart

Also known as: contribution graph, activity calendar, GitHub heatmap

Show change over timeShow distribution TemporalNumerical Matrix/Grid

Description

A calendar heatmap arranges data on a grid that mirrors the structure of a calendar: columns represent weeks, rows represent days of the week (Monday through Sunday), and each cell is colored by the magnitude of a value for that day. The result is a dense, familiar layout that reveals daily patterns, weekly rhythms, seasonal trends, and anomalies at a glance. This chart form was popularized by GitHub’s contribution graph and has since become a widely recognized way to show activity over time.

The calendar layout leverages two powerful perceptual mechanisms simultaneously. First, the spatial regularity of the grid makes it easy to spot clusters, streaks, and gaps — a week of high activity appears as a bright vertical band, while weekday vs. weekend differences emerge as horizontal stripes. Second, the calendar metaphor is universally understood, so readers can immediately map cells to specific dates without consulting an axis.

Calendar heatmaps work best for data with daily granularity over periods of months to a few years. They excel at showing patterns that are tied to the calendar structure itself: day-of-week effects, holiday dips, seasonal cycles, and year-over-year comparisons. For data at finer granularity (hourly) or coarser granularity (monthly), other chart forms like line graphs or standard heatmaps are more appropriate.

Calendar Heatmap — daily activity over one year

When to Use

  • Showing daily activity, contributions, or events over a year or more
  • Revealing day-of-week patterns, seasonal trends, and holiday effects
  • Tracking habits, exercise, commits, sales, or any metric with daily granularity
  • When the calendar metaphor adds context (readers care about specific dates)

When NOT to Use

  • For sub-daily data (hourly, minute-level) — use a heatmap with hour/minute axes
  • When precise value comparison matters more than pattern detection — use a line graph or bar chart
  • For data spanning many years at daily resolution — the chart becomes very wide; consider year-by-year panels
  • When the data has no meaningful day-of-week or seasonal structure — the calendar layout adds no insight

Anatomy

  • Day cells: Small rectangles or rounded squares, one per day, colored by value intensity.
  • Week columns: Vertical stacks of 7 cells, one per week of the year.
  • Day-of-week rows: Horizontal alignment groups (Mon-Sun) enabling weekday/weekend comparison.
  • Month labels: Text above or below the grid marking month boundaries.
  • Color scale: A sequential scale (light to dark) mapping low to high values. A legend is essential.
  • Year label: Identifies which year (or date range) the calendar covers.

Variations

  • Multi-year calendar: Multiple years stacked vertically for year-over-year comparison.
  • Circular calendar: Days arranged in a spiral or radial layout to emphasize cyclical patterns.
  • Categorical calendar: Cells colored by category (e.g., type of activity) rather than a continuous scale.
  • Interactive calendar: Clicking a cell shows detailed data for that day, or brushing selects a range of days.
  • Month-grid calendar: A traditional calendar layout with cells arranged by month grids rather than continuous week columns.

Code Reference

// Observable Plot - calendar heatmap
Plot.plot({
  marks: [
    Plot.cell(data, {
      x: d => d3.utcWeek.count(d3.utcYear(d.date), d.date),
      y: d => d.date.getUTCDay(),
      fill: "value",
      tip: true,
      inset: 0.5
    })
  ],
  x: { axis: null },
  y: {
    tickFormat: d => ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d],
    label: null
  },
  color: {
    scheme: "Greens",
    legend: true,
    label: "Contributions"
  }
})