Installation
pnpm dlx shadcn@latest add https://dithercn.dev/r/dissolve-text.jsonUsage
import { DissolveText } from "@/components/dithercn/dissolve-text";
export function Headline() {
return (
<DissolveText as="h2" accent="blue" className="text-6xl font-semibold">
Arrives one dot at a time
</DissolveText>
);
}Each character is masked by a 4×4 Bayer matrix that fills in level by level, from no dots to solid, so the letters build up out of the dither. By default it plays once when the text scrolls into view, one character after another. Use the replay button on the preview to see it again.
direction sets the order: start (reading order), end, center (from the middle out), edges (from both ends in) or random, a shuffle that is the same on every visit. accent gives each unit a colour while it arrives, fading to the text colour over twice the duration.
fill sets what the letters settle into. solid ends as plain text. gradient stops at a dither level (level, 0.625 by default). dotted and hatched end in the style kit's textures, which suit large, heavy type.
Examples
Hatched poster type
fill="hatched" and direction="center" on a heavy display face, with the line below following from the middle out.
Word by word
by="word" reveals whole words, which reads better for a paragraph. An 8×8 matrix with 2px cells gives a finer grain at body sizes, and the heading arrives in green.
Dissolving out and in
Set show={false} and the text dissolves back out. Here a timer hides the word, swaps it, and shows the next one in a random order. Each letter arrives white and settles into the ink colour.
Props
| Prop | Type | Default |
|---|---|---|
| children* The text. | Type: string | Default – |
| as The element to render. | Type: "h1" | "h2" | "h3" | "h4" | "p" | "span" | "div" | Default "span" |
| by Dissolve each character, each word, or the whole text at once. | Type: "char" | "word" | "line" | Default "char" |
| direction The order units dissolve in. | Type: "start" | "end" | "center" | "edges" | "random" | Default "start" |
| trigger Play when mounted, or when the text first scrolls into view. | Type: "mount" | "in-view" | Default "in-view" |
| show false dissolves the text out. true brings it back. | Type: boolean | Default true |
| color Text colour: a palette name or any CSS colour. | Type: DitherColor | Default the text colour |
| accent Colour each unit has while it arrives, fading to the text colour. | Type: DitherColor | "none" | Default "none" |
| fill What the letters settle into. | Type: "solid" | "gradient" | "dotted" | "hatched" | Default "solid" |
| level Gradient only: the coverage the letters settle at, 0 to 1. | Type: number | Default 0.625 |
| duration Milliseconds for one character, word or line. | Type: number | Default 600 |
| stagger Milliseconds between one unit and the next. | Type: number | Default 28 per character, 90 per word |
| delay Milliseconds before the first unit starts. | Type: number | Default 0 |
| speed Multiplies every timing. 2 plays twice as fast. | Type: number | Default 1 |
| matrix The threshold pattern the letters fill in through. | Type: "bayer2" | "bayer4" | "bayer8" | Default "bayer4" |
| cell Size of one dither cell in CSS pixels. | Type: number | Default --dither-cell |
Notes
- The text stays real text, one inline element per character or word, so it can be selected, copied and indexed. While it is dissolved out, it is hidden from assistive technology.
- The animation is plain CSS keyframes that step a
mask-imagethrough the matrix's levels with an ease-out timing. A text that plays on mount needs no JavaScript to run. - With reduced motion turned on, the text appears and disappears instantly. A textured
fillstill applies. - Text waiting for
in-viewstays hidden until it scrolls into view. Without JavaScript it is shown as normal text.