# ActivityGrid (/docs/charts/activity-grid)

Each cell is one slot in the series, shaded by intensity: the contribution-graph shape, read for cadence, streaks, and
seasonality. There are five levels — a faint empty track plus four intensity steps — and the four steps share the exact
opacity ramp HeatCell and HeatStrip use, so "how intense" means the same thing across the library. Level 0 is the empty
track, because a slot with no activity is empty rather than a bottom-of-scale value. The steps are discrete bins, never
a continuous gradient: a continuous ramp implies precision a handful of pixels can't deliver.

```tsx
import { ActivityGrid } from "@microcharts/react/activity-grid";

const commitCounts = [ 0, 1, 2, 1, 3, 4, 2, 0, 1, 3, 2, 4, 3, 1, 0, 2, 4, 3, 2, 1, 3, 0, 2, 3, 4, 1, 2, 0, ];

<ActivityGrid data={commitCounts} title="Commit activity" />
```

## Install

```tsx
import { ActivityGrid } from "@microcharts/react/activity-grid";

<ActivityGrid data={commits} title="Commits" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.

## Try it

```tsx
import { ActivityGrid } from "@microcharts/react/activity-grid/interactive";

<ActivityGrid
  data={/* 35 values */}
  layout="grid"
  cell={12}
/>
```

## When to use it

Use it for daily activity, streaks and cadence, and seasonality. Avoid it for exact values or for a precise comparison
between two individual cells: color intensity is deliberately approximate, so pair it with a number when precision
matters.

## Sizing

ActivityGrid sizes from `cell` — the edge length of one square in viewBox units. Bump it to scale the whole grid, or
drive the width from CSS to fill a container; the viewBox keeps the grid's aspect ratio.

**default cells**

```tsx
// ActivityGrid sizes from cell edge length (default 10)
<ActivityGrid data={commits} />
```

**larger cells**

```tsx
// bump every cell — the whole grid scales with it
<ActivityGrid data={commits} cell={14} />
```

**responsive**

```tsx
// let CSS drive the width — the viewBox keeps the grid's ratio
<div style={{ width: "100%", maxWidth: 320 }}>
  <ActivityGrid data={commits} style={{ width: "100%", height: "auto" }} />
</div>
```

## Variants

`layout="strip"` collapses to a single row for inline use.

```tsx
const commits = [0, 1, 2, 1, 3, 4, 2, 0, 1, 3, 2, 4, 3, 1, 0, 2, 4, 3, 2, 1];

<ActivityGrid data={commits} />
<ActivityGrid data={commits.slice(0, 12)} layout="strip" />
```

`shape` swaps the cell mark without changing the data or the levels. `"round"` softens the grid for product surfaces;
`"dot"` adds space between cells in dense strips.

```tsx
const commits = [0, 1, 2, 1, 3, 4, 2, 0, 1, 3, 2, 4, 3, 1, 0, 2, 4, 3, 2, 1];

<ActivityGrid data={commits} shape="round" />
<ActivityGrid data={commits.slice(0, 12)} layout="strip" shape="dot" />
```

`anchor` aligns the grid to the real calendar: the first column pads down to the weekday of the given day (UTC), so rows
read as weekdays. Weeks start on Monday by default (`weekStart={1}`, matching the ISO calendar); `weekStart={0}`
switches to Sunday without touching the data. Cells fill column-major — each column is a week, top to bottom — which is
what makes the grid read as a calendar rather than an arbitrary sequence.

```tsx
// data[0] is Thursday 1970-01-01 → three leading empty slots
<ActivityGrid
  data={[2, 3, 1, 4, 2, 0, 1, 3, 2, 4, 3, 1, 0, 2, 4, 3, 2, 1]}
  anchor="1970-01-01"
/>
```

## Edge cases

```tsx
<ActivityGrid data={[]} title="No history" />
```

```tsx
<ActivityGrid data={[0, 0, 0, 0, 0, 0, 0]} title="Quiet week" />
```

```tsx
<ActivityGrid data={[3]} title="One day" />
```

## Four homes

**In a sentence**

```tsx
<p>
  Commit cadence for the last two weeks{" "}
  <span className="mc-inline">
    <ActivityGrid data={commits.slice(0, 14)} layout="strip" cell={7} summary={false} />
  </span>{" "}
  — bursts of shipping, punctuated by two dead-quiet days.
</p>
```

**In a table cell**

```tsx
<td>
  <ActivityGrid data={commits.slice(0, 7)} layout="strip" cell={8} />
</td>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">67</span>
  <span className="unit">across 35 days, busiest at 4</span>
  <ActivityGrid data={commits} cell={8} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  api <ActivityGrid data={commits.slice(-14)} layout="strip" cell={5} />
</button>
```

## Accessibility

Intensity is a color channel, so ActivityGrid always pairs it with a numeric summary of its total and peak. That summary
survives forced-colors and color-blind viewing. The interactive entry adds 2-D arrow-key navigation, announcing each
cell's value as you move. Give it an `anchor` and every cell is a dated day, so the readout names the date rather than a
position ("Mar 2: 12").

The interactive entry follows the shared [interaction contract](/docs/accessibility#one-interaction-contract):
arrow keys rove between units on both axes, `Home` and `End` jump to the ends, and a click, tap, `Enter` or
`Space` selects a unit — pinning its readout so it survives blur, until you select it again, press `Escape`, or
press outside the chart. On touch, a tap pins and a drag scrubs.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `data` (required) | `number[]` | Ordered values, one per cell. |
| `layout` | `"grid" \| "strip"` | 7-row calendar or single strip. |
| `shape` | `"square" \| "round" \| "dot"` | Cell mark: crisp square, soft corners, or padded dot. |
| `anchor` | `string \| Date` | First slot's calendar day (UTC) — pads the first column so weekday rows align. |
| `weekStart` | `0 \| 1` | Start of week for anchor alignment (0 Sunday, 1 Monday). |
| `cell` | `number` | Cell edge length in viewBox units. |
| `steps` | `number` | Intensity steps including the zero track (default 5, GitHub-like). Same knob, same name, as CalendarStrip, CoverageStrip, GardenGrid, HeatCell, HeatStrip and SpiralYear. |
| `domain` | `[number, number]` | Explicit range for level bucketing. |
| `title` | `string` | Accessible name; joins the auto summary. |
| `summary` | `string \| false` | Override or disable the auto summary. |
| `gap` | `number` | Gap between cells in viewBox units (default 2). |
| `animate` | `boolean` | (interactive) Opt-in entrance motion when the chart mounts client-side — add `import "@microcharts/react/motion"` once. Inert on the server, on hydrated server HTML, and under `prefers-reduced-motion`. |

Plus the shared grammar — `data`, `domain`, `color`, `title`, `summary`, `format` — and the layout props (`width`, `height`, `className`, `style`) that every chart accepts. Interactive entries also share `animate` and `live`, and — wherever a chart has more than one navigable unit — `onActive`, `onSelect`, `selectedIndex` and `defaultSelectedIndex`; and — wherever the chart shows a hover value — `readout`. See [the shared grammar](/docs/quickstart#the-shared-grammar).
