Skip to content

Components / Pointer

Ink Reveal

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.

Live demo

A poster: the word liquid over an orange disc in concentric rings

Ink.

move your cursor

Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.

Measured

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

Baseline · widely available

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.

Install Pro

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:

terminal
npx shadcn@latest add @moonarc-pro/ink-reveal
src/pages/index.astro
<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.

Props

PropTypeDefaultDescription
colorstring'oklch(0.22 0.02 260)'Cover colour; any CSS colour, theme tokens included.
grainstringcolor lifted 15 % toward whiteA second tone mixed into the cover as a soft grain.
radiusnumber0.5Brush radius as a share of the box.
curlnumber18How much the ink swirls.
fadenumber0.35How fast the cover comes back, per second. 0 keeps the reveal.
roughnumber0.5How ragged the ink's edge is, 0–1.

Reduced motion

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.

With ClientRouter

Bound 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.

Why it is built this way

Replaces: Mask reveal / ink cursor (React Bits Pro) · Scratch card reveal. See the migration table.

Spotlight

Pointer

A soft radial light follows the cursor over a card and fades in only while hovered.

every browserpointerhover

Draws smoke that follows the cursor.

every browserpointeralways

Makes an image turn liquid under the cursor and settle back.

every browserpointer

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown