2DTime patternsGuide 02

Punchcard heatmaps for weekday and hour patterns

Show when work happens across the week so teams can plan around real rhythms instead of averages.

Commits throughout the week

Example data
MonWedFriSun
0:006:0012:0018:00
lessmore
7 days × 24 hoursHover or focus a cell for details
Rows
Weekday
Columns
Hour of the day
Value
Commit count

The pattern

What the data tells you

Afternoon peaks reveal the team's working rhythm, with a shorter activity window on Saturday.

A punchcard maps weekdays to hours. Circle size and colour can encode the same value twice, making busy windows visible even when the chart is printed or viewed by someone with colour-vision differences.

Aggregating a whole week into one number hides the handoff, support, and release windows that teams actually need to discuss. A punchcard keeps both axes visible and makes peaks easy to compare across days.

Build it in React

From data to heatmap

TypeScript / React

Prepare your data

A dense matrix where each cell represents one weekday-hour pair, such as commits, support tickets, incidents, or check-ins.

Make the component yours

Start with this example. Adjust the labels, colours, and tooltips to fit your product.

punchcard.tsx
import { Heatmap } from "@thilakbhat/heatmap-ui";
import "@thilakbhat/heatmap-ui/styles.css";

const DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

// 7 rows (Monday first) × 24 columns (hours), counted from commit timestamps.
function toPunchcard(timestamps: string[]) {
  const grid = DAYS.map(() => Array<number>(24).fill(0));
  for (const timestamp of timestamps) {
    const date = new Date(timestamp);
    grid[(date.getDay() + 6) % 7][date.getHours()] += 1;
  }
  return grid;
}

export function CommitPunchcard({ timestamps }: { timestamps: string[] }) {
  return (
    <Heatmap
      rows={7}
      columns={24}
      values={toPunchcard(timestamps)}
      shape="circle"
      encode="both"
      cellSize={10}
      gap={2}
      rowLabels={DAYS}
      columnLabels={[0, 6, 12, 18].map((hour) => ({ column: hour, text: `${hour}:00` }))}
      tooltip={(cell) => `${DAYS[cell.row]} ${cell.column}:00, ${cell.value} commits`}
      showLegend
    />
  );
}
Key props
Heatmapshape="circle"encode="both"columnLabels
API reference

A few more details

Common questions

Punchcard heatmaps, explained.

What does a punchcard heatmap show?

A punchcard heatmap places weekdays on one axis and hours or time slots on the other, making recurring work, support, commit, or attendance patterns easy to compare.

When should I use a punchcard instead of a calendar heatmap?

Use a punchcard for repeated intraday patterns such as Monday morning support volume. Use a calendar heatmap when the important question is how activity changes from date to date.

How can a punchcard encode activity magnitude?

Use colour, circle size, or both to encode the value. Encoding the same measure in two visual channels keeps high-volume windows readable beyond colour alone.

Can I use a punchcard heatmap for commits or support tickets?

Yes. Aggregate each event into a weekday-hour cell, pass the resulting seven-by-24 matrix to Heatmap, and add row or column labels for the time ranges your team uses.