# Sparkline

A tiny inline trend with a dithered fill and a dot on the latest value, for stat cards and table rows.

> 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/sparkline-demo.tsx" controls="sparklineControls">
  <SparklineDemo />
</ComponentPreview>

## Installation [#installation]

<InstallBlock name="sparkline" />

## Usage [#usage]

```tsx
import { Sparkline } from "@/components/dithercn/sparkline";

export function RevenueTrend() {
  return (
    <Sparkline
      data={[41, 43, 42, 46, 48, 47, 51, 54]}
      label="Revenue, last 8 weeks"
      className="w-24"
    />
  );
}
```

A sparkline takes a plain array of numbers, oldest first. Its height is a prop and its width follows the element, so set it with `className` or let it fill a table cell. By default the value range fits the data, because a sparkline shows the shape of a trend. Pass the same `domain` to every sparkline in a column when the rows should be compared.

`label` is required. Screen readers hear it followed by a summary of the data, such as "Revenue, last 8 weeks: 41 to 54, low 41, high 54".

## Examples [#examples]

### In a table [#in-a-table]

Each row's trend sits in its own cell at 24px high. `dot={false}` keeps a column of sparklines quiet.

<ComponentPreview src="examples/sparkline-table.tsx">
  <SparklineTable />
</ComponentPreview>

### Line only, one in ink [#line-only-one-in-ink]

`area={false}` drops the fill. Each row shares `domain={[0, 180]}`, so a flat service draws flat instead of stretching its small wobble to full height, and the one spike drawn in ink is the first thing you see.

<ComponentPreview src="examples/sparkline-line.tsx">
  <SparklineLine />
</ComponentPreview>

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` (required) | `readonly number[]` |  | Values in order, oldest first. |
| `label` (required) | `string` |  | What the trend shows. Starts the accessible summary of the data. |
| `height` | `number` | `32` | Height in px. The width follows the element. |
| `color` | `DitherColor` | `"ink"` | A palette name such as "green", or any CSS colour. |
| `area` | `boolean` | `true` | Fill under the line, densest just below it. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | Texture of the fill. gradient is densest just under the line. |
| `bloom` | `"off" \| "low" \| "high"` | `"off"` | A soft glow in the line's colour behind it. |
| `interactive` | `boolean` | `false` | Scrub with the pointer or arrow keys to read single values in a small label. |
| `animate` | `boolean` | `true` | Entrance motion. Always off under reduced motion. |
| `dot` | `boolean` | `true` | Mark the latest value with a dot. |
| `level` | `number` | `0.5` | Gradient only: coverage of the fill just under the line, from 0 to 1. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The dither texture. |
| `cell` | `number` | `--dither-cell minus 1` | Size of one dither cell in px, rounded to whole device pixels. Smaller than other charts so the texture reads at small sizes. |
| `domain` | `[min: number, max: number]` | `The data's range` | The value range the height covers. Share one across a column so rows compare. |
| `format` | `Intl.NumberFormatOptions` | `Compact, as in 1.2K` | How values are written in the accessible summary. |

## Notes [#notes]

* By default the sparkline is a single image to assistive technology, named by `label` and a summary of its first, last, lowest and highest values, with no readout; show the exact number next to it. With `interactive` it becomes a focusable group: the arrow keys move a gliding dot and a polite live region reads each value.
* On entrance the line draws in, the fill fades up and the dot appears last. With reduced motion it appears finished.
* It runs no animation loop and costs one small SVG, so a table with dozens of rows stays cheap.