# Area chart

A line over a dithered fill that is densest at the line, for revenue, traffic and other totals over time.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The dithercn agent skill, for building any dither on the engine, is at [/.well-known/agent-skills/dithercn/SKILL.md](/.well-known/agent-skills/dithercn/SKILL.md).

<ComponentPreview src="examples/area-chart-demo.tsx" size="lg" controls="areaChartControls">
  <AreaChartDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="area-chart" />

## Usage [#usage]

```tsx
import { AreaChart } from "@/components/dithercn/area-chart";

const data = [
  { month: "Jan", revenue: 18400 },
  { month: "Feb", revenue: 21900 },
  { month: "Mar", revenue: 26300 },
];

export function Revenue() {
  return (
    <AreaChart
      data={data}
      x="month"
      series={[{ label: "Revenue", value: "revenue" }]}
    />
  );
}
```

`data` can hold any objects. `x` names the field with each point's label and every series names the field with its value. Both also accept a function, such as `(datum) => datum.total / 100`, when you render the chart from a Client Component. The chart fills its container's width and redraws when the container resizes.

## Examples [#examples]

### Two layered series [#two-layered-series]

With two or more series the chart adds a legend, and every series after the first draws in the next palette colour. Each fill knocks out the ones behind it, so overlapping areas never mix and the lines always sit on top. Scrub with the pointer, or focus the chart and use the arrow keys: a crosshair and readout glide between points. Hover a legend entry to spotlight that series, click it to keep it lit, and press Escape to let go.

<ComponentPreview src="examples/area-chart-layered.tsx" size="lg">
  <AreaChartLayered />
</ComponentPreview>

### Compact, inside a stat card [#compact-inside-a-stat-card]

`axes={false}` drops the axes and the padding around the plot, so the fill can run to the edges of a card. `fill="dotted"` swaps the gradient for a sparse, even lattice of dots.

<ComponentPreview src="examples/area-chart-compact.tsx">
  <AreaChartCompact />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` (required) | `readonly T[]` |  | One entry per point along the x axis, in order. |
| `x` (required) | `keyof T \| ((datum: T) => string \| number)` |  | The field (or function) holding each point's x axis label. |
| `series` (required) | `[ChartSeries<T>, ...ChartSeries<T>[]]` |  | Each series is { label, value, color? }, where value is a numeric field of T or a function. Later series draw on top. |
| `stack` | `"none" \| "stacked" \| "percent"` | `"none"` | none overlaps the series from the baseline. stacked sits each on the ones before it. percent stacks and scales every point to 100%; the readout adds each share. |
| `height` | `number` | `240` | Height in px, axes included. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | gradient is densest just under the line and thins toward the axis. dotted is a sparse lattice, hatched diagonal lines, solid flat colour. |
| `bloom` | `"off" \| "low" \| "high"` | `"off"` | A soft glow in each series' colour behind its fill. Its strength follows --dither-bloom, lower in light mode. |
| `colors` | `readonly DitherColor[]` | `ink, orange, green, pink, …` | Colours by series position: palette names such as "green" or any CSS colour. A series' own color wins. |
| `interactive` | `boolean` | `true` | Scrubbing, the readout and the legend spotlight. false makes a decorative chart: no focus stop, no readout, just an image with a summary. |
| `animate` | `boolean` | `true` | Entrance motion. Always off under reduced motion. |
| `level` | `number` | `0.625` | Gradient only: coverage of the densest band, just under the line, from 0 to 1. |
| `curve` | `"smooth" \| "linear"` | `"smooth"` | smooth is a monotone curve that never overshoots a data point. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | Gradient only: the dither texture. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in px, rounded to whole device pixels. |
| `axes` | `boolean` | `true` | Show the value and category axes. |
| `tooltip` | `boolean` | `true` | Show the readout while scrubbing. |
| `legend` | `boolean` | `Two or more series` | Show the legend under the chart. Its entries spotlight a series on hover and lock it on click. |
| `format` | `Intl.NumberFormatOptions \| ((value: number) => string)` | `Compact, as in 1.2K` | How values are written on the axis, in the readout and in the table. Options work from Server Components. |

Series colours follow the palette: ink, then orange, green, pink, purple, blue, red and grey. Pass `colors` for the whole chart, or `color` on one series, using a palette name or any CSS colour.

## Notes [#notes]

* The chart is a focusable group labelled with a short summary, and the full data sits in a visually hidden table, so screen readers get every value. The arrow keys, Page Up, Page Down, Home and End move the readout without easing, a polite live region reads out the values at each stop, and Escape clears the readout and any locked series. Legend entries are toggle buttons.
* On entrance the line draws in and the fill steps up through the dither levels. With reduced motion the finished chart appears at once.
* Cells are snapped to whole device pixels, so the dither stays sharp on any screen. Markers use a ring in `--dither-paper`; set it to your card's background if the card is not the page colour.
* It is plain SVG with no chart library. The entrance re-renders the chart about 16 times. Scrubbing updates at most once a frame and only moves the crosshair, markers and readout, by transform, so a year of daily points in three series stays smooth.