GodUIGodUI
91

Anatomy of Spotlight Reveal

How an inverted radial mask punches a hole through a cover layer, eased toward the pointer on a single rAF loop — with idle breathing and click-to-pin.

SpotlightReveal stacks two full-bleed layers. The reveal prop sits underneath; children is the cover. An inverted radial mask on the cover makes a soft hole that follows the pointer — exposing whatever's beneath without unmounting either layer.

Reveal under, cover on top

Both layers are absolute inset-0. The cover owns the mask; the reveal is always painted and simply shows through the hole. Clicking the root toggles a pinned ref so the spotlight can freeze in place.

Anatomyreveal under · cover masked

reveal

cover

masked

Reveal
absolute inset-0 underneath — the hidden layer
Cover
children on top, punched by the radial mask
Together
mask hole follows --x/--y on the cover
tsx
 
<div className="absolute inset-0">{reveal}</div>

<div
  ref={coverRef}
  className="absolute inset-0"
  style={
    reduce
      ? { opacity: 0.9 }
      : { WebkitMaskImage: maskImage, maskImage }
  }
>
  {children}
</div>

Softness is the hard edge

softness (0–1, default 0.55) sets how wide the feather is. inner = radius * (1 - softness) is the fully transparent core; from there the mask ramps to opaque at radius. Hard edges keep the hole crisp; soft ones feel like a flashlight bloom.

Softnessinner = radius × (1 − softness)

transparent 0 → transparent inner → #000 radius

Hard edge
softness → 0 · inner ≈ radius
Soft feather
softness 0.55 · inner = radius × 0.45
tsx
 
const inner = Math.max(0, radius * (1 - softness));
const maskImage =
  `radial-gradient(circle ${radius}px at var(--x, 50%) var(--y, 50%), ` +
  `transparent 0, transparent ${inner}px, #000 ${radius}px)`;

Eased follow, idle breathing, pin

A single requestAnimationFrame loop owns motion. Each frame:

  1. If idle (!hovering && !pinned), retarget onto a slow ellipse — cos(t·0.6)·18%w / sin(t·0.8)·16%h around center.
  2. Ease: cur += (target − cur) * ease (default 0.16).
  3. Write --x / --y on the cover with setProperty.

Pointer enter/move updates target (unless pinned). Click flips pinned. No React state for position — same cheap path as SpotlightCard, plus the lag that makes the light feel liquid.

Follow + breatherAF ease 0.16 · idle ellipse

cur.x += (target.x - cur.x) * ease

Idle drift
ellipse when !hovering && !pinned
Eased follow
cur += (target − cur) × 0.16 per frame
Pinned
click toggles — target freezes until unpin
tsx
 
cur.current.x += (target.current.x - cur.current.x) * ease;
cur.current.y += (target.current.y - cur.current.y) * ease;
cover.style.setProperty("--x", `${cur.current.x}px`);
cover.style.setProperty("--y", `${cur.current.y}px`);

Reduced motion

useReducedMotion() skips the rAF entirely. The cover drops the mask and sits at opacity: 0.9 — both layers readable as a calm blend, no breathing, no chase.

tsx
 
style={
  reduce
    ? { opacity: 0.9 }
    : { WebkitMaskImage: maskImage, maskImage }
}

The result

Two layers, one inverted mask, a 0.16 ease, and an idle ellipse that invites the hover. Move to follow; click to pin.

Resultthe real component — hover to follow, click to pin

GODUI

Cover layer

Move to reveal

On this page

Built with GodUI

Beautifully crafted motion components for modern interfaces.

Star on GitHub