# Contours

A topographic map drawn as a dither: contour lines over slowly drifting hills, with heavier index lines and a light hillshade.

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

## Installation [#installation]

```bash
npx shadcn@latest add https://dithercn.dev/r/contours.json
```

## Usage [#usage]

```tsx
import { Contours } from "@/components/dithercn/contours";

export function Section() {
  return (
    <section className="relative isolate min-h-[480px]">
      <Contours className="absolute inset-0 -z-10" />
      <h2>Your heading</h2>
    </section>
  );
}
```

Contour lines trace even steps of height across a field of low hills. Every fifth line is drawn heavier, as on a survey map, and slopes facing away from a light in the top left pick up a light stipple. The hills drift and slowly change shape. They keep their size in pixels, so a narrow box shows a smaller piece of the same map rather than a squashed one.

## Examples [#examples]

### Survey map [#survey-map]

More `lines`, a smaller `scale` and a little more `relief` in green and brown give an old ordnance survey sheet. `cell={2}` keeps the lines fine.

```tsx title="examples/contours-survey.tsx"
import { Contours } from "@/components/dithercn/contours";

export default function ContoursSurvey() {
  return (
    <Contours
      lines={18}
      scale={0.8}
      relief={0.6}
      cell={2}
      palette={[
        "--dither-paper",
        "oklch(0.62 0.09 145)",
        "oklch(0.48 0.11 55)",
      ]}
      className="absolute inset-0"
    />
  );
}
```

### Behind a headline [#behind-a-headline]

Set `index` and `relief` to 0 for plain lines, and pass two soft tints of the ink so the lines never reach full contrast.

```tsx title="examples/contours-headline.tsx"
import { Contours } from "@/components/dithercn/contours";

export default function ContoursHeadline() {
  return (
    <section className="relative isolate flex size-full flex-col items-start justify-end gap-3 px-6 pb-12 sm:px-10 sm:pb-16">
      <Contours
        lines={9}
        index={0}
        relief={0}
        speed={0.6}
        palette={[
          "--dither-paper",
          "color-mix(in oklch, var(--dither-ink) 20%, var(--dither-paper))",
          "color-mix(in oklch, var(--dither-ink) 38%, var(--dither-paper))",
        ]}
        className="absolute inset-0 -z-10"
      />
      <h3 className="max-w-md text-3xl font-medium tracking-[-0.03em] text-balance">
        Know the ground before you build on it
      </h3>
      <p className="text-muted-foreground max-w-sm text-sm text-pretty">
        Thin lines in two soft tints, with no index lines and no shading, stay
        quiet behind text.
      </p>
    </section>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `lines` | `number` | `14` | How many contour lines from the lowest ground to the highest, from 4 to 32. |
| `scale` | `number` | `1` | Size of the hills. Higher values pack more of them in. |
| `index` | `number` | `5` | Every how many lines one is drawn heavier. 0 draws them all the same. |
| `relief` | `number` | `0.5` | How strongly slopes facing away from the light are shaded, from 0 to 1. |
| `palette` | `[DitherColor, DitherColor, ...DitherColor[]]` | `Paper, a tint of --dither-ink, then --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"` | `"bayer4"` | The threshold pattern. See Dither engine. |
| `cell` | `number` | `--dither-cell` | Size of one dither cell in CSS pixels. |
| `speed` | `number` | `1` | How fast the hills drift. |
| `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 map 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 faint CSS rings.