Features
- Organic edge — a circle of colour spreads from the point you click, its rim wobbled by
feTurbulenceandfeDisplacementMap, so it reads as ink rather than a geometric wipe. - Pure CSS and SVG — no canvas, no animation library, zero runtime dependencies. ESM, tiny.
- Framework-agnostic — one function and an empty element. It works the same in a React app, a Vite site or plain HTML.
- Three modes —
coverto leave a page,revealto arrive on one,veilto hold a quiet wash while something loads. - Promise-based —
spill()resolves when the ink has landed, so navigating is just the next line. - Respects reduced motion — under
prefers-reduced-motionit becomes a plain fade, with no spreading edge.
Install
npm install @kud/inkblotUsage
Give the page one empty element to act as the overlay. Inkblot does the rest.
<div id="overlay" aria-hidden="true"></div>Cover the page from a click, then navigate under it:
import { spill } from "@kud/inkblot"
const overlay = document.querySelector<HTMLElement>("#overlay")!
document.addEventListener("click", async (event) => {
const link = (event.target as Element).closest<HTMLAnchorElement>("a[href]")
if (!link) return
event.preventDefault()
await spill(overlay, { mode: "cover", from: link, colour: "#2b1d14" })
window.location.href = link.href
})Arrive on the new page and lift the ink. Call this before first paint if the page should start covered:
import { clear, spill } from "@kud/inkblot"
const overlay = document.querySelector<HTMLElement>("#overlay")!
await spill(overlay, { mode: "reveal", colour: "#2b1d14" })
window.addEventListener("pageshow", (event) => {
if (event.persisted) clear(overlay)
})