Hover✨ Premium

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.

CSS MaskVanilla JSHover

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

Quelval e-commerce product card — revealing the back of a t-shirt on hover — Fluid X-Ray Reveal example 1

① Quelval e-commerce product card — revealing the back of a t-shirt on hover

WhenThe shopper hovers over the product image area to progressively reveal the back of the garment under their cursor.
WhyA fluid revealer is more engaging than a plain 'see back' button: the shopper explores the surface of the zone freely.
SettingsEffect zone 232 px tall in a card; .xr-scene at height: 232px; aspect-ratio: unset; layers as URL-encoded SVG background-image.
Full-screen hero section for Vellona Realty — day-to-night landscape comparison — Fluid X-Ray Reveal example 2

② Full-screen hero section for Vellona Realty — day-to-night landscape comparison

WhenThe visitor hovers the background with a large radius to progressively reveal the night version beneath the day version.
WhyA full-screen radial revealer showcases two moods of the same neighbourhood (calm day / lively night) in a single interactive zone.
SettingsRADIUS raised to 180 px for a wider reveal; layers as CSS linear gradients (sky + city silhouette); nav and hero text as pointer-events: none overlays.
Phelora photo portfolio — before/after retouching comparator on a portrait — Fluid X-Ray Reveal example 3

③ Phelora photo portfolio — before/after retouching comparator on a portrait

WhenThe visitor hovers the portrait in the portfolio to locally reveal the retouched version and assess the precision of the edit.
WhyA pinpoint revealer is more subtle than a classic before/after slider: it lets the visitor explore areas of interest at their own pace.
Settings.xr-scene at width: 220px; height: 220px; aspect-ratio: unset; RADIUS reduced to ~70 px for a precise reveal.

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, the xr-revealed class 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), pointermove during 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.

Chrome 120+✓ Full (measured)
Firefox 53+✓ Full (not measured, webkit prefix handled)
Safari 15.4+✓ Full (not measured, webkit prefix handled)
Edge 120+✓ Full (Chromium engine)
Mobile iOS 15.4+✓ Full (not measured)
Android Chrome 120+✓ Full (measured on Chromium)

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):

index.html — structure
<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 / propertyDefaultEffect
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

Yes. Add 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.
Replace pointermove with pointerdown and remove the continuous update: add the xr-revealed class on click (scene.addEventListener('click', () => scene.classList.add('xr-revealed'))).