KQ-1386 · DITHER/CURTAIN

Dither Curtain

A veil of ordered dots over any content: where it is partly drawn back, cells switch on and off by a Bayer threshold, and the dots coarsen through nested grids toward the middle of the dissolving edge, so the content appears through a regular screen rather than a blur. The reveal is a tween across a ragged front (a dissolve is a clip, never a spring) and covering again is a shorter exit tween; while veiled, a mouse or pen pushes a dithered hole through it that trails the pointer and opens on springs.glide. With the hover trigger the reveal grows from the pointer, and keyboard focus anywhere inside lifts it the same way.

KQ-1386 · DITHER/CURTAIN

Basinworks · private beta

The quiet water report

Readings from every basin, sent at dawn.

412 on the list

veiled · move over it to peek

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/dither-curtain

Props

children
ReactNode
default —
The content behind the veil; it stays fully interactive and in the accessibility tree throughout.
matrix
"2x2" | "4x4" | "8x8"
default "4x4"
The ordered-dither matrix: 2×2 is a coarse check, 8×8 a fine, even screen.
scale
number
default 4
The finest dot, in CSS pixels.
coarsen
number
default 0.6
How much the dots coarsen toward the middle of the dissolving edge, from one size at 0 to up to 8× scale at 1.
shape
"square" | "round"
default "square"
Square cells, or round dots that grow until a full veil is solid.
color
string
default "var(--accent)"
The veil's colour, resolved on the element so it follows the theme.
trigger
"mount" | "inView" | "hover" | "manual"
default "inView"
When the veil draws back: on mount, when scrolled into view, on hover or keyboard focus, or when revealed turns true.
revealed
boolean
default —
For trigger="manual": true draws the veil back, false brings it over again.
once
boolean
default true
Reveal only once; false lets the veil come back when the piece leaves the view (inView) or the pointer and focus leave (hover).
origin
"left" | "right" | "top" | "bottom" | "center" | "pointer"
default "left" ("pointer" with the hover trigger)
Where the dissolve starts.
duration
number
default 900
How long the reveal takes, ms; covering again takes 0.6× as long.
delay
number
default 120
Wait before a mount or in-view reveal, ms.
softness
number
default 0.4
How wide the dithered band at the edge is, 0 to 1.
ragged
number
default 0.4
How ragged the edge is, from a straight line at 0 to 1.
holeRadius
number
default 64
The radius of the hole a mouse or pen pushes through the veil, px; 0 turns it off.
interactive
boolean
default true
The pointer pushes a hole through the veil (mouse and pen only), and with the hover trigger the wrapper takes keyboard focus.
seed
number
default 11
Seed for the edge's raggedness.
dpr
number
default 2
The highest device pixel ratio the canvas draws at (never above 2).
onRevealChange
(revealed: boolean) => void
default —
Fires when a reveal or a re-cover completes, with whether the content is now revealed.
className
string
default —
Classes for the wrapper; give it the content's radius so the veil follows it.