# Dither gradient

A linear, radial or conic gradient drawn as a dither, for heroes, section breaks and cards.

> 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/dither-gradient.json](https://dithercn.dev/r/dither-gradient.json), under `files[].content`.

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { DitherGradient } from "@/components/dithercn/dither-gradient";

export function Hero() {
  return (
    <section className="relative isolate min-h-[480px]">
      <DitherGradient angle={160} className="absolute inset-0 -z-10" />
      <h1>Your headline</h1>
    </section>
  );
}
```

The gradient fills its box, so give it a size with `className`. It runs from paper at the start to ink at the end, in your theme's `--dither-paper` and `--dither-ink` colours.

## Examples [#examples]

### Glow behind a headline [#glow-behind-a-headline]

A radial gradient that starts as ink and fades to paper. With the centre below the bottom edge and a small radius, only a low arc of light shows, and the headline sits on clean paper.

```tsx title="examples/dither-gradient-radial.tsx"
import { DitherGradient } from "@/components/dithercn/dither-gradient";

export default function DitherGradientRadial() {
  return (
    <section className="relative isolate flex size-full flex-col items-center gap-3 px-6 pt-14 text-center sm:pt-20">
      <DitherGradient
        mode="radial"
        center={[0.5, 1.15]}
        radius={0.5}
        from={1}
        to={0}
        className="absolute inset-0 -z-10"
      />
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance">
        Release notes that people read
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        A glow that rises from the bottom edge and leaves the headline on clean
        paper.
      </p>
    </section>
  );
}
```

### Conic sweep through a palette [#conic-sweep-through-a-palette]

`palette` takes any number of colours from paper to ink. A conic sweep jumps from its last colour back to its first, so this palette starts and ends on the background to hide the seam. Blue noise gives it an even, grainy texture.

```tsx title="examples/dither-gradient-conic.tsx"
import { DitherGradient } from "@/components/dithercn/dither-gradient";

export default function DitherGradientConic() {
  return (
    <DitherGradient
      mode="conic"
      angle={45}
      matrix="blue-noise"
      palette={[
        "--background",
        "--chart-1",
        "--chart-3",
        "--chart-1",
        "--background",
      ]}
      noise={0.15}
      className="absolute inset-0"
    />
  );
}
```

### Still, inside a card [#still-inside-a-card]

With `animate={false}` the gradient draws one frame and stops. `to={0.55}` keeps the densest band light enough for text to sit on, and setting `--dither-paper` to the card's own background blends the gradient into it.

```tsx title="examples/dither-gradient-static.tsx"
import { DitherGradient } from "@/components/dithercn/dither-gradient";

export default function DitherGradientStatic() {
  return (
    <div className="bg-background relative isolate flex w-full max-w-xs [--dither-paper:var(--background)] flex-col gap-1 overflow-hidden rounded-2xl p-5">
      <DitherGradient
        angle={0}
        from={0}
        to={0.55}
        matrix="bayer8"
        cell={4}
        animate={false}
        className="absolute inset-x-0 bottom-0 -z-10 h-2/3"
      />
      <span className="text-muted-foreground text-sm">Weekly active</span>
      <span className="text-3xl font-semibold tracking-tight tabular-nums">
        12,480
      </span>
      <span className="text-muted-foreground mt-10 text-xs">
        Up 8% on last week
      </span>
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | `"linear" \| "radial" \| "conic"` | `"linear"` | The gradient's shape. |
| `angle` | `number` | `180 (linear), 0 (conic)` | Linear: the direction in degrees, as in CSS. Conic: where the sweep starts. |
| `center` | `[x: number, y: number]` | `[0.5, 0.5]` | Radial and conic only. The centre as fractions of the box, measured from the top left. |
| `radius` | `number` | `1` | Radial only. 1 reaches the farthest corner. |
| `from` | `number` | `0` | Tone at the start, from 0 (paper) to 1 (ink). |
| `to` | `number` | `1` | Tone at the end. |
| `noise` | `number` | `0.12` | How far a slow noise drift bends the bands. 0 gives straight, even bands. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `["--dither-paper", "--dither-ink"]` | Colours from paper to ink: palette names such as "green", CSS variable names or any CSS colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. See Dither engine. |
| `levels` | `number` | `All the matrix can draw` | Tones the gradient snaps to before dithering. Lower values give visible bands. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast the noise drifts. |
| `animate` | `boolean` | `true` | false stops the motion: a still frame if it starts off, the current frame if it was moving. |
| `className` | `string` | `–` | Size and position. The gradient fills this box. |

## Notes [#notes]

* The gradient is decorative and hidden from assistive technology. Put real text on top of it, not inside it.
* With reduced motion turned on, it draws one still frame and stops.
* It pauses when scrolled out of view or when the tab is hidden, and releases its WebGL context when far offscreen. Without WebGL it shows a smooth CSS gradient in the same colours.
* One gradient costs one small draw per frame at 30 fps, at one pixel per dither cell. See [Performance](/docs/engine/performance).