# Dither fade

A dithered edge that fades a section's content into a solid colour, for the seam between two sections or the end of a clipped list.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { DitherFade } from "@/components/dithercn/dither-fade";

export function Changelog() {
  return (
    <section className="relative overflow-hidden">
      <ChangelogEntries />
      <DitherFade side="bottom" size={200} />
    </section>
  );
}
```

Put the fade inside a `relative` parent. It covers `size` along `side` and paints `color` over whatever is underneath: sparse cells on the content side, solid at the edge. The default colour is `--dither-paper`, your page background.

It is still and drawn with CSS masks, so it renders on the server and uses no WebGL. Use as many as you like on one page.

## Examples [#examples]

### Between two sections [#between-two-sections]

Give the fade the colour of the section below it, or leave the default when that section uses the page background. The upper section dissolves into the next one with no hard line.

```tsx title="examples/dither-fade-sections.tsx"
import { DitherFade } from "@/components/dithercn/dither-fade";

export default function DitherFadeSections() {
  return (
    <div className="flex size-full flex-col">
      <section className="bg-primary text-primary-foreground relative flex flex-1 flex-col items-center justify-center gap-2 px-6 pb-16 text-center">
        <h3 className="text-2xl font-medium tracking-[-0.03em] text-balance">
          Start free, upgrade when you ship
        </h3>
        <p className="max-w-sm text-sm text-pretty opacity-80">
          The blue section dissolves into the page below it.
        </p>
        <DitherFade size={96} cell={4} />
      </section>
      <section className="flex flex-1 items-center justify-center px-6">
        <p className="text-muted-foreground text-sm">Pricing table goes here</p>
      </section>
    </div>
  );
}
```

### Both ends of a row [#both-ends-of-a-row]

`side="left"` and `side="right"` fade a scrolling row at its ends. Different sizes on each side hint at which way there is more to see.

```tsx title="examples/dither-fade-sides.tsx"
import { DitherFade } from "@/components/dithercn/dither-fade";

const TAGS = [
  "Bayer 2",
  "Bayer 4",
  "Bayer 8",
  "Blue noise",
  "Floyd–Steinberg",
  "Atkinson",
  "Sierra Lite",
  "Palettes",
  "Cells",
] as const;

export default function DitherFadeSides() {
  return (
    <div className="relative w-full max-w-md overflow-hidden py-2">
      <ul className="flex w-max gap-2">
        {TAGS.map((tag) => (
          <li
            key={tag}
            className="border-border text-muted-foreground rounded-full border px-3 py-1 text-sm whitespace-nowrap"
          >
            {tag}
          </li>
        ))}
      </ul>
      <DitherFade side="left" size={48} />
      <DitherFade side="right" size={96} />
    </div>
  );
}
```

### Drifting edge [#drifting-edge]

With `animate` the edge wanders slowly, drawn with WebGL. Keep it for one hero-sized seam; the still fade is the better default.

```tsx title="examples/dither-fade-drift.tsx"
import { DitherFade } from "@/components/dithercn/dither-fade";

export default function DitherFadeDrift() {
  return (
    <div className="relative size-full overflow-hidden">
      <div className="bg-primary absolute inset-0" />
      <DitherFade side="top" size="70%" animate matrix="bayer8" cell={4} />
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | The edge of the parent the fade sits on. The colour is solid there. |
| `size` | `number \| string` | `160` | Depth of the fade. A number is CSS pixels; a string is any CSS length, such as "30%". |
| `color` | `DitherColor` | `"--dither-paper"` | The colour the content fades into: a palette name such as "grey", a CSS variable name, or any CSS colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. See Dither engine. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `animate` | `boolean` | `false` | true lets the edge drift slowly, drawn with WebGL. |
| `className` | `string` | `–` | Extra classes for the fade's box. |

## Notes [#notes]

* The fade is decorative and hidden from assistive technology. Content under it stays in the page and is still read out, so do not hide anything important beneath it.
* The still fade has no motion. With `animate`, reduced motion holds one still frame, and it pauses offscreen like the other backgrounds.
* The still fade is 16 masked layers and no script. The animated one costs one small draw per frame and one WebGL context.