KQ-1407 · PHANTOM/CURSOR

Phantom Cursor

A shadow self that follows the pointer inside its box: a soft form of rising smoke shaped like the pointer's own arrow (or a round wisp), drawn by one WebGL2 shader scissored to its own bounds. It trails on a 2D spring that slides from springs.glide to one softer than springs.drift, smearing along its travel, then stiffens up to five times and hurries in to condense under the cursor once the hand rests, and dissolves after an idle moment. Keyboard focus inside sends it to rest on the focused element's corner.

KQ-1407 · PHANTOM/CURSOR

Waylight · night edition

Somebody is right behind you.

phantom asleep · move across the card, or tab to it

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/phantom-cursor

Props

children
ReactNode
default —
The surface the phantom haunts; it stays fully interactive.
lag
number
default 0.6
How far it lags the hand: 0 keeps close on the glide spring, 1 trails on a spring softer than drift.
size
number
default 28
The phantom's height, in CSS pixels.
smoke
number
default 0.6
How much of it is smoke: 0 is a soft silhouette, 1 a loose, drifting wisp.
shape
"arrow" | "orb"
default "arrow"
A shadow of the pointer's own arrow, or a round wisp.
stretch
number
default 0.6
How far it smears behind itself along its travel, 0 to 1.
catchUp
number
default 0.6
How much faster it closes in once the hand rests, 0 (never hurries) to 1 (five times stiffer).
idle
number
default 1400
How long it lingers once caught up and still, in ms, before it dissolves; 0 never dissolves while the pointer is inside.
offset
[number, number]
default [8, 10]
Where it rests relative to the pointer, in CSS pixels; a shadow falls below and to the right.
color
string
default "var(--ink)"
Its colour, any CSS colour, resolved on the element.
opacity
number
default 0.45
Its peak opacity, 0 to 1.
followFocus
boolean
default true
Keyboard focus inside sends the phantom to the focused element.
interactive
boolean
default true
The pointer (mouse and pen) summons it; touch gets the resting state.
paused
boolean
default false
Holds the smoke's drift; the phantom still follows.
seed
number
default 3
Seed for the smoke.
dpr
number
default 2
The highest device pixel ratio the canvas draws at (never above 2).
className
string
default —
Classes for the box.
ref
Ref<PhantomCursorHandle>
default —
Exposes appear(x?, y?), which shows the phantom resting at a point in the box (its centre when omitted) until it dissolves; ignored while the visitor's pointer is inside.