# HeatStrip (/docs/charts/heat-strip)

HeatStrip prints one stepped intensity cell per point along a single timeline: it is the 1×N sibling of ActivityGrid,
with the same discrete step scale and the same cell vocabulary. A slot with no record renders a hairline outline,
visibly different from a low value, because empty is not zero.

Square cells with a density-adaptive gap keep boundaries legible where round shapes blur at 10 px. Every real value
renders at a visible opacity (0.25–1), and the faint track look is reserved for empty slots. The steps stay discrete,
the `domain` is shared rather than autoscaled per row, and downsampling takes the max per bucket rather than the mean.

```tsx
<HeatStrip
  data={[12, 25, 38, 52, 66, 79, 88, 90, 84, 71, 55, 40, 28, 45, 62, 78, 85, 74, 58, 35]}
  domain={[0, 100]}
  title="Load per hour"
  width={240}
  height={20}
/>
```

## Install

```tsx
import { HeatStrip } from "@microcharts/react/heat-strip";

<HeatStrip data={hourlyLoad} domain={[0, 100]} title="Load per hour" />
```

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 { HeatStrip } from "@microcharts/react/heat-strip/interactive";

<HeatStrip
  data={hourlyLoad}
  domain={[0, 100]}
/>
```

## When to use it

Use it for per-tenant load rows and intensity ribbons in dense tables. For exact shape use Sparkline; for weekday
rhythm, ActivityGrid.

## Sizing

**shared-domain rows**

```tsx
// one domain per table — rows stay comparable
{tenants.map((t) => (
  <HeatStrip key={t.id} data={t.load} domain={[0, 100]} />
))}
```

**nulls hold their slot**

```tsx
// a missing record is visibly different from zero
<HeatStrip data={[3, null, 8, null, 5]} />
```

## Variants

```tsx
const load = [12, 38, 66, 88, 84, 55, 28, 62, 85, 58];

<HeatStrip data={load} domain={[0, 100]} shape="round" />
<HeatStrip data={load} domain={[0, 100]} shape="dot" />
```

## Edge cases

```tsx
<HeatStrip data={[3, null, 8, null, 5]} />
```

A `null` holds its slot as a hairline outline instead of collapsing the strip, so positions stay aligned across stacked
rows.

## Four homes

**In a sentence**

```tsx
<p>
  API load over the last 20 minutes{" "}
  <span className="mc-inline">
    <HeatStrip data={hourlyLoad} domain={[0, 100]} height={14} summary={false} />
  </span>{" "}
  — peaked at 90%, closed at 35%.
</p>
```

**In a table cell**

```tsx
// one shared domain — rows stay comparable
{tenants.map((t) => (
  <tr key={t.name}>
    <td>{t.name}</td>
    <td><HeatStrip data={t.load} domain={[0, 100]} /></td>
  </tr>
))}
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">90%</span>
  <span className="unit">peak, minute 8</span>
  <HeatStrip data={hourlyLoad} domain={[0, 100]} width={200} height={30} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  Acme <HeatStrip data={hourlyLoad} domain={[0, 100]} height={12} />
</button>
```

## Accessibility

The summary reuses `describeSeries` verbatim: for the load strip above that is **"Trending up 383%. Range 12 to 88. Last
value 58."** A color ramp is never the only channel. The interactive entry roves cells with ActivityGrid-parity
announcements (**"Point 8 of 20: 90."**, empty slots as "no data").

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 \| null)[]` | Time-ordered values; null = no record (≠ zero). |
| `steps` | `number` | Shared step-scale granularity (default 5). |
| `shape` | `"square" \| "round" \| "dot"` | Shared cell vocabulary. |
| `domain` | `[number, number]` | Cross-row calibration — share one domain per table. |
| `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).
