# Dither glow

A soft dithered spotlight from an edge, a corner or any point, for heroes, pricing cards and quiet section backgrounds.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { DitherGlow } from "@/components/dithercn/dither-glow";

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

The glow fills its box and shines from `position`. It uses `bayer8`, whose 64 tones keep the falloff smooth. Its centre is brightest and it falls off softly, with a slow breath and a little noise at the edge so it never looks like a flat circle.

## Examples [#examples]

### Rising behind a headline [#rising-behind-a-headline]

With `position="bottom"` the light sits just below the edge, so only its top shows. The headline sits near the top, on clean paper.

```tsx title="examples/dither-glow-hero.tsx"
import { DitherGlow } from "@/components/dithercn/dither-glow";

export default function DitherGlowHero() {
  return (
    <section className="relative isolate flex size-full flex-col items-center gap-3 px-6 pt-14 text-center sm:pt-20">
      <DitherGlow
        position="bottom"
        radius={0.45}
        intensity={0.9}
        className="absolute inset-0 -z-10"
      />
      <p className="text-muted-foreground font-mono text-xs tracking-wide uppercase">
        Now in beta
      </p>
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance sm:text-4xl">
        Ship the changelog with the release
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        Light rises from below the fold and stops short of the copy.
      </p>
    </section>
  );
}
```

### Corner of a card [#corner-of-a-card]

A corner glow is the quietest way to lift one card out of a row. Setting `--dither-paper` to the card's background blends the glow into it, and `bayer8` gives a finer grain at small sizes.

```tsx title="examples/dither-glow-corner.tsx"
import { DitherGlow } from "@/components/dithercn/dither-glow";

export default function DitherGlowCorner() {
  return (
    <div className="bg-background border-border relative isolate flex w-full max-w-xs [--dither-paper:var(--background)] flex-col gap-4 overflow-hidden rounded-2xl border p-6">
      <DitherGlow
        position="top-right"
        radius={0.55}
        intensity={0.45}
        matrix="bayer8"
        className="absolute inset-0 -z-10"
      />
      <div className="flex flex-col gap-1">
        <span className="text-sm font-medium">Team</span>
        <span className="text-muted-foreground text-sm">
          For groups that ship every week.
        </span>
      </div>
      <div className="flex items-baseline gap-1">
        <span className="text-4xl font-semibold tracking-tight tabular-nums">
          $24
        </span>
        <span className="text-muted-foreground text-sm">per seat / month</span>
      </div>
      <ul className="text-muted-foreground flex flex-col gap-1.5 text-sm">
        <li>Unlimited projects</li>
        <li>Shared palettes</li>
        <li>Priority support</li>
      </ul>
    </div>
  );
}
```

### Any point, any palette [#any-point-any-palette]

`position` also takes `[x, y]` as fractions of the box. With a longer palette the glow steps through each colour from its edge to its core.

```tsx title="examples/dither-glow-palette.tsx"
import { DitherGlow } from "@/components/dithercn/dither-glow";

export default function DitherGlowPalette() {
  return (
    <DitherGlow
      position={[0.3, 0.35]}
      radius={0.55}
      intensity={1}
      palette={[
        "--dither-paper",
        "color-mix(in oklch, var(--dither-ink) 30%, var(--dither-paper))",
        "oklch(0.6 0.2 305)",
        "--dither-ink",
        "color-mix(in oklch, var(--dither-ink) 45%, var(--foreground))",
      ]}
      cell={4}
      className="absolute inset-0"
    />
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `position` | `"top" \| "bottom" \| "left" \| "right" \| "center" \| "top-left" \| "top-right" \| "bottom-left" \| "bottom-right" \| [x: number, y: number]` | `"top"` | Where the light comes from. Edges sit just outside the box; a point is measured from the top left. |
| `radius` | `number` | `0.7` | How far the light reaches. 1 reaches the farthest corner. |
| `intensity` | `number` | `0.75` | Brightness at the centre, from 0 (none) to 1 (full ink). |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `["--dither-paper", "--dither-ink"]` | Colours from paper to ink: palette names such as "blue", CSS variable names, or any CSS colour. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer8"` | The threshold pattern. See Dither engine. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast it moves. |
| `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 background fills this box. |

## Notes [#notes]

* It 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 CSS approximation in the same colours.
* It costs one small draw per frame at 30 fps, at one pixel per dither cell. See [Performance](/docs/engine/performance).