Fluid X-Ray Reveal
The cursor triggers a radial-gradient CSS mask updated in real time — a trail of 22 fading circles progressively reveals a hidden layer beneath the first. The ring and hint disappear on the first hover. Reduced motion handled: the hidden layer is shown immediately.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The .fx-0776-root wrapper centres the scene .xr-scene[data-xr], whose data-base attribute gives the content of the visible layer (.xr-base) and data-xray the hidden layer (.xr-xray). Both layers are stacked in position: absolute; inset: 0; the hidden layer has a higher z-index and is masked by its initial mask-image at radius 0 off-screen.
The script maintains a trail of 22 points: each frame, the smoothed position (smooth) moves toward the cursor by a 0.18 LERP. Local velocity determines each circle's radius (between 66 and 220 px). The trail is converted into a series of merged radial-gradient(circle …) using mask-composite: add, creating a fluid multi-circle mask. Each trail point's opacity decreases by 0.02 per frame until it reaches 0, then the point is removed.
A visual ring (.xr-ring) and a hint (.xr-hint) guide the user before the first hover; they disappear as soon as the pointer enters the scene. Under prefers-reduced-motion: reduce, the hidden layer is shown in full without a loop (xr-revealed). The scene responds to pointermove (mouse and touch via the Pointer Events API).
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, thexr-revealedclass is added immediately, the mask is removed and the hidden layer displays in full with no loop. The hint and ring are hidden. - Touch: the effect uses
pointermove, which covers mouse, stylus and touch. On a touchscreen, sliding a finger across the zone reveals the hidden layer. On Safari iOS (< 17),pointermoveduring a touch may require a held press — test on a real device. - Keyboard: no keyboard interaction is built in. The hidden layer is not accessible without a pointer. If both layers carry functional information, expose the content of both layers as alternative text (
aria-label) or as visible elements nearby. - Layer content: both layers are read by screen readers in HTML order (base then x-ray). If both carry the same information (before/after), indicate the change in accessible text (
aria-live).
Browser compatibility
CSS mask-image with mask-composite: add + WebkitMaskComposite: source-over + Pointer Events API + matchMedia, no dependency. mask-composite: add available since Chrome 120, Firefox 53 and Safari 15.4 (with -webkit-mask-composite: source-over prefix for earlier versions). Not measured in Firefox and WebKit.
Without JavaScript, the mask stays at its initial state (radius 0, hidden layer not visible). On Chrome < 120 and Firefox < 53, mask-composite: add is not supported: the multi-circle mask is not rendered; only the last circle of the trail is applied.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0776-root"><div class="xr-scene" data-xr="" data-base="🧑" data-xray="🧛"><div class="xr-layer xr-base">🧑</div><div class="xr-layer xr-xray">🧛</div><div class="xr-ring"></div><div class="xr-hint">Help text</div></div></div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Mask radius (JS — RADIUS = 110) |
110 px | 60 px for a precise, pinpoint revealer; 180 px for a wider reveal. |
Trail length (JS — TRAIL = 22) |
22 points | 10 points for a short, discreet trail; 35 for a more persistent brush effect. |
Fade speed (JS — FADE = 0.02) |
0.02 per frame | 0.01 for a slowly fading trail; 0.05 for one that disappears almost instantly. |
Layer content (HTML — data-base and data-xray) |
Emojis 🧑 and 🧛 | Pass image URLs or HTML strings. For images, add background-image to the .xr-base and .xr-xray layers in CSS and leave data-base / data-xray empty. |
Ring and hint colour (CSS — rgba(124, 255, 170, …)) |
Light green #7cffaa |
Replace with your primary colour to integrate the effect into your brand identity. |
FAQ
background-image, background-size: cover and background-position: center to the .xr-base and .xr-xray layers in CSS, and leave data-base / data-xray empty — the script only sets textContent on those layers when the attributes are non-empty.mask-composite: add is supported since Chrome 120, Firefox 53 and Safari 15.4. On earlier versions, the mask applies only the last trail circle — the effect is less fluid but still functional.pointermove with pointerdown and remove the continuous update: add the xr-revealed class on click (scene.addEventListener('click', () => scene.classList.add('xr-revealed'))).