# Dither card

A card with a dithered glow in a corner or along an edge and a dithered rim on its border, which can grow through the levels on hover.

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

## Installation [#installation]

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

## Usage [#usage]

```tsx
import { DitherCard } from "@/components/dithercn/dither-card";
import { CardContent, CardHeader, CardTitle } from "@/components/ui/card";

export function Revenue() {
  return (
    <DitherCard glow="top-right" interactive>
      <CardHeader>
        <CardTitle>Monthly revenue</CardTitle>
      </CardHeader>
      <CardContent>$48,210</CardContent>
    </DitherCard>
  );
}
```

`DitherCard` has the same spacing, border and colours as shadcn's `Card`, so `CardHeader`, `CardContent` and `CardFooter` fit inside it. It does not depend on them: plain markup works too.

The glow is a stack of rings, each filled with a denser dither than the one below it. The rim is a wider stack masked to a band one cell wide inside the border: it is dense where the glow sits and thins out round the card, so the edge catches the light. Set `radius` if you round the card differently, so the rim follows the corners. With `interactive`, a second, larger stack switches on ring by ring when the card is hovered or something inside it has focus, so the glow grows outward through the levels.

## Examples [#examples]

### Corners and edges [#corners-and-edges]

`glow` takes any corner, or `top` and `bottom` for a wide glow along an edge. Hover each card to see it grow.

```tsx title="examples/dither-card-glows.tsx"
import { DitherCard } from "@/components/dithercn/dither-card";
import type { DitherCardGlow } from "@/components/dithercn/dither-card";
import { cn } from "@/lib/utils";

const CARDS: readonly { glow: DitherCardGlow; title: string; body: string }[] =
  [
    {
      body: "Ship a component with one command.",
      glow: "top",
      title: "Install",
    },
    {
      body: "Every colour follows your theme.",
      glow: "top-right",
      title: "Theme",
    },
    {
      body: "Still frames under reduced motion.",
      glow: "bottom-left",
      title: "Respect",
    },
  ];

export default function DitherCardGlows() {
  return (
    <div className="grid w-full max-w-3xl gap-3 sm:grid-cols-3">
      {CARDS.map((card) => (
        <DitherCard
          key={card.glow}
          glow={card.glow}
          reach={130}
          interactive
          className={cn(
            "bg-background min-h-52 px-5",
            card.glow.startsWith("bottom") ? "justify-start" : "justify-end"
          )}
        >
          <div className="flex flex-col gap-1">
            <span className="font-mono text-xs text-muted-foreground">
              {card.glow}
            </span>
            <span className="font-semibold tracking-tight">{card.title}</span>
            <span className="text-muted-foreground text-sm">{card.body}</span>
          </div>
        </DitherCard>
      ))}
    </div>
  );
}
```

### Colour and texture [#colour-and-texture]

`color` takes a palette name or any CSS colour, and `fill` changes the texture. On the left, green with blue noise and 2 pixel cells for a fine, even grain. On the right, orange with a hatched fill: every ring shares the hatching, and where the rings overlap near the edge the lines build up.

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

export default function DitherCardPalette() {
  return (
    <div className="grid w-full max-w-2xl gap-3 sm:grid-cols-2">
      <DitherCard
        glow="bottom-right"
        color="green"
        matrix="blue-noise"
        strength={0.6}
        reach={240}
        cell={2}
        className="bg-background min-h-56 px-6"
      >
        <span className="font-semibold tracking-tight">
          Green, blue noise, 2px cells
        </span>
        <span className="text-muted-foreground -mt-4 text-sm">
          A finer grain for denser layouts.
        </span>
      </DitherCard>
      <DitherCard
        glow="bottom"
        color="orange"
        fill="hatched"
        reach={170}
        className="bg-background min-h-56 px-6"
      >
        <span className="font-semibold tracking-tight">Orange, hatched</span>
        <span className="text-muted-foreground -mt-4 text-sm">
          Diagonal lines that build up towards the edge.
        </span>
      </DitherCard>
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `glow` | `"top-left" \| "top-right" \| "bottom-left" \| "bottom-right" \| "top" \| "bottom"` | `"top-left"` | Where the glow sits. |
| `reach` | `number` | `240` | How far the glow spreads from its origin, in pixels. |
| `strength` | `number` | `0.5` | Densest dither level, at the origin, from 0 to 1. |
| `rim` | `boolean` | `true` | Light the border with dither, densest near the glow. |
| `radius` | `number` | `14` | Corner radius in pixels. The rim follows it. |
| `interactive` | `boolean` | `false` | Grow the glow on hover and when the card has focus inside. |
| `color` | `DitherColor` | `"ink"` | Glow colour: a palette name such as "green", a CSS variable name, or any CSS colour. |
| `fill` | `"gradient" \| "dotted" \| "hatched" \| "solid"` | `"gradient"` | Texture of the glow. Gradient steps through dither levels; the others stack one texture and fade it by overlap. |
| `matrix` | `"bayer2" \| "bayer4" \| "bayer8" \| "blue-noise"` | `"bayer4"` | The threshold pattern. |
| `cell` | `number` | `3` | Size of one dither cell in CSS pixels. |

## Notes [#notes]

* The glow is decorative and hidden from assistive technology. The card itself is a plain `div`; make the content inside it, not the card, the link or button.
* The hover glow only runs on devices that can hover. Keyboard users get the same effect when focus moves inside the card.
* With reduced motion turned on, the hover glow switches on in one step instead of ring by ring.
* The glow is static SVG, so it renders on the server and costs nothing while idle.