Spotlight
PointerA soft radial light follows the cursor over a card and fades in only while hovered.
Covers content with a layer that the cursor washes away like ink in water, showing what is underneath. The content is ordinary HTML in the slot, in the page for readers, search and keyboard users from the start. The cover is a canvas above it, drawn by the GPU: a fluid simulation whose dye is the hole, with a ragged, moving edge and a soft grain. The ink fades back at `fade` per second (0 keeps what was revealed). A small swirl opens at the centre on the first frame to say there is something underneath. Focus inside, reduced motion, a touch device without hover, no WebGL2 or no JavaScript all show the content with no cover.

Ink.
move your cursor
Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.
Adjust · props are custom properties, set inline, so you edit the running instance
JavaScript with its libs
12.5 kB raw
1.6 kB raw of its own, 1.0 kB gzip. The rest is the runtime, the canvas helper, the GL core and the fluid solver, loaded once for every effect on the page.
CSS 4.7 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 56 · Firefox 51 · Safari 15; needs webgl2. Elsewhere: without WebGL2 or a colour-renderable float texture, on a device without hover, or with reduced motion or focus inside, no cover: the content shows as it is.
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/ink-reveal<InkReveal class="aspect-video">
<img src="/poster.webp" alt="…" class="h-full w-full object-cover" />
</InkReveal>Without a key the registry answers 401 with an upgrade message. Related free primitives: spotlight.
| Prop | Type | Default | Description |
|---|---|---|---|
color | string | 'oklch(0.22 0.02 260)' | Cover colour; any CSS colour, theme tokens included. |
grain | string | color lifted 15 % toward white | A second tone mixed into the cover as a soft grain. |
radius | number | 0.5 | Brush radius as a share of the box. |
curl | number | 18 | How much the ink swirls. |
fade | number | 0.35 | How fast the cover comes back, per second. 0 keeps the reveal. |
rough | number | 0.5 | How ragged the ink's edge is, 0–1. |
No cover at all: the content shows as it is. The canvas is hidden by the media query, and the page's own motion switch removes it through the runtime.
ClientRouterBound through the shared runtime: loop, observers and pointer listener are released before the swap and the WebGL context is lost on purpose after it; five round trips leave one context. Under transition:persist the canvas keeps its context and starts again on the next page.
Replaces: Mask reveal / ink cursor (React Bits Pro) · Scratch card reveal. See the migration table.
A soft radial light follows the cursor over a card and fades in only while hovered.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown