# Stat card

A KPI card with a headline number, a coloured delta and a dithered trend along the bottom.

> 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).

> **This is an example, not the only option.** It is one component built on the dithercn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `DitherShader` ([Write your own dither](/docs/engine/write-your-own.md)), and read this example's source for technique. Its full source is in [https://dithercn.dev/r/stat-card.json](https://dithercn.dev/r/stat-card.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://dithercn.dev/r/stat-card.json
```

## Usage [#usage]

```tsx
import { StatCard } from "@/components/dithercn/stat-card";

export function Revenue() {
  return (
    <StatCard
      label="Monthly recurring revenue"
      value="$48,210"
      delta={12.4}
      deltaLabel="vs last month"
      trend={[31.2, 34.6, 37.8, 41.2, 45.1, 48.2]}
    />
  );
}
```

`delta` is a percentage: `12.4` reads as +12.4%. It is green when it moves the `good` way and red when it doesn't, using the palette's green and red pulled slightly toward the text colour so the small text keeps its contrast. `trend` takes recent values, oldest first, and draws them as a line with a square marker on the latest value. Under it, `fill="gradient"` draws bands that thin out below the line in dither steps; `dotted`, `hatched` and `solid` fill the area evenly. `color` takes a palette name such as `"blue"`, and `bloom` adds a soft glow behind the trend.

The card uses `bg-card` and a faint dot surface in the ink colour that fades out from the top right corner. Pass a `className` to change its background or width.

## Examples [#examples]

### KPI row [#kpi-row]

Four cards in a grid. Latency and error rate set `good="down"`, so latency going up is red and error rate going down is green. Each card has its own `color`, and latency, the one moving the wrong way, uses `color="red"` with `fill="hatched"` and a low bloom.

```tsx title="examples/stat-card-grid.tsx"
import { StatCard } from "@/components/dithercn/stat-card";

export default function StatCardGrid() {
  return (
    <div className="grid w-full max-w-3xl gap-3 sm:grid-cols-2">
      <StatCard
        label="Active users"
        value={12_480}
        delta={8.1}
        deltaLabel="vs last week"
        trend={[9.8, 10.2, 10.1, 10.9, 11.4, 11.2, 11.9, 12.5]}
        color="blue"
        className="bg-background"
      />
      <StatCard
        label="p95 latency"
        value="184 ms"
        delta={9.6}
        good="down"
        deltaLabel="vs last week"
        trend={[162, 158, 166, 171, 169, 177, 190, 184]}
        color="red"
        fill="hatched"
        bloom="low"
        className="bg-background"
      />
      <StatCard
        label="Error rate"
        value="0.42%"
        delta={-31.2}
        good="down"
        deltaLabel="vs last week"
        trend={[0.71, 0.66, 0.69, 0.58, 0.55, 0.49, 0.44, 0.42]}
        color="green"
        fill="dotted"
        className="bg-background"
      />
      <StatCard
        label="Trial conversion"
        value="18.6%"
        delta={1.3}
        deltaLabel="vs last week"
        trend={[17.1, 17.4, 17.2, 17.9, 18.1, 17.8, 18.3, 18.6]}
        color="purple"
        className="bg-background"
      />
    </div>
  );
}
```

### Without a trend [#without-a-trend]

Leave `trend` out for a compact card, and `delta` out when there is nothing to compare. A delta of 0 is grey with no arrow.

```tsx title="examples/stat-card-compact.tsx"
import { StatCard } from "@/components/dithercn/stat-card";

export default function StatCardCompact() {
  return (
    <div className="grid w-full max-w-xl gap-3 sm:grid-cols-3">
      <StatCard
        label="Deploys"
        value={146}
        delta={4.3}
        deltaLabel="this week"
        className="bg-background"
      />
      <StatCard
        label="Open incidents"
        value={0}
        delta={0}
        deltaLabel="no change"
        className="bg-background"
      />
      <StatCard label="Queue depth" value={1204} className="bg-background" />
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` |  | What the number is. |
| `value` (required) | `number \| string` |  | The headline number. Numbers get thousands separators; strings show as given. |
| `delta` | `number` | `–` | Change since the last period, in percent. |
| `deltaLabel` | `string` | `–` | What the change compares with, such as "vs last month". |
| `good` | `"up" \| "down"` | `"up"` | Which direction counts as good, for the delta colour. |
| `trend` | `number[]` | `–` | Recent values, oldest first. Needs at least two. |
| `color` | `DitherColor` | `"ink"` | Colour of the trend and the dot surface, a palette name or any CSS colour. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | Texture under the trend line. |
| `bloom` | `"off" \| "low" \| "high"` | `"off"` | Soft glow in the colour behind the trend. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | Threshold pattern of the gradient fill. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |

## Notes [#notes]

* Screen readers hear the label, the value and the delta in words, such as "Up 12.4% vs last month". The trend and the surface are decorative and hidden.
* The trend is drawn at the card's real width in CSS pixels, so cells stay square and crisp at any size. It redraws when the card resizes.
* Nothing moves, so there is nothing to reduce.