Moving Mask
A soft halo that follows the mouse, a finger or the arrow keys and lets you see, through a radial mask, the layer hidden under the surface.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →
3 usage examples



How it works
The .mvm-zone holds two layers: .mvm-front, the visible surface, and .mvm-layer, the hidden layer placed on top. That layer only shows through mask-image: radial-gradient(circle var(--mvm-radius) at var(--mvm-x) var(--mvm-y), #000, transparent) (plus its -webkit- version): solid at the centre of the halo, it fades out gradually up to the radius, 60 px by default. It feels like a torch rather than a sharp hole.
At rest the halo is off (layer at opacity 0): only the surface reads. When the mouse comes in, the script puts the halo centre under the pointer, as a percentage of the zone, and adds the .mvm-on class: the layer fades in over 0.25 s and the halo then follows every move with no lag. It switches off when the mouse leaves. Both layers take your content: text, or images and SVG with the .mvm-media class, which covers the whole layer — that is how you reveal a photo or a plan under another picture.
With a finger, the halo lights up where the finger lands, follows it while dragging and goes out when it lifts; for that, the zone blocks page scrolling (touch-action: none, adjustable with --mvm-touch). With the keyboard, the zone (tabindex="0") lights the halo on focus and moves it with the arrow keys: 5% of the zone per press, 15% with Shift.
Each zone keeps its own position, set as inline style on it: several zones in one root or several copies of the effect work separately. Nothing is added to window.
Accessibility
- Keyboard: Tab lights the halo (
#c4b5fdoutline), the arrow keys move it, Shift + arrow goes faster. Arrows only scroll the page when focus is not on the zone. - Screen readers: the mask hides nothing from assistive technology: text in both layers is read. If the layer repeats the surface (a button with a doubled label), add
aria-hidden="true"to.mvm-layer. A game based on sight (finding an object) should offer its own text equivalent. - Reduced motion: under
prefers-reduced-motion: reducethe fade-in goes away. The halo stays tied to the user's gesture, which is the interaction itself. - Touch: finger down = halo on, finger up = halo off. On a large zone in a scrolling page, set
--mvm-touch: pan-y: vertical scrolling keeps working, at the cost of a halo that no longer follows the finger during that scroll.
Browser compatibility
Measured in Chromium 151, Firefox 153 and Playwright's WebKit 26.5: 0 errors; the 2nd copy on the page reacts on its own; keyboard focus and arrows; finger down then dragged (touch pointer events); no animation with reduced motion; nothing overflowing at 390 px. :focus-visible sets the minimum versions; the mask is also written with the -webkit- prefix, which Chrome needs before version 120.
Older browsers: mouse and finger work in older versions that have the inset property (Chrome 87, Firefox 66, Safari 14.1: the versions that brought the inset property used to stack the faces); only lighting up on keyboard focus is missing. Without JavaScript the halo never lights up and only the surface shows.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0625-root">
<div class="mvm-zone" tabindex="0">
<span class="mvm-front">Move mouse</span>
<div class="mvm-layer">
<span>Hidden!</span>
</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 |
|---|---|---|
--mvm-w and --mvm-h |
280px and 180px |
Zone size (width capped by the container). |
--mvm-corner |
1rem |
Zone corner rounding. |
--mvm-radius |
60px |
Halo radius. The layer is solid at the centre and fades out up to this radius. |
--mvm-front and --mvm-front-color |
linear-gradient(135deg,#1e1e2e,#2d2d44) and #9a9aa2 |
Background and text of the visible surface. |
--mvm-front-y and --mvm-front-pad |
flex-end and 0 1rem 1rem |
Where the surface text sits: at the bottom by default, so it does not overlap the hidden text; center and 0 for a button. |
--mvm-layer and --mvm-layer-color |
linear-gradient(135deg,#6366f1,#8b5cf6,#d946ef) and #fff |
Background and text of the layer the halo reveals. |
--mvm-touch |
none |
touch-action value on the zone. none: the finger moves the halo. pan-y or auto: the finger scrolls the page. |
--mvm-bg and --mvm-min-h |
#0a0a0f and 300px |
Root background and minimum height. |
.mvm-media class |
— | On an image, SVG or video inside a layer: the media covers it entirely, any text goes on top. |
FAQ
.mvm-front and the second in .mvm-layer, each as <img class="mvm-media"> with the same framing. The halo then shows the second picture exactly where the pointer is.--mvm-touch to pan-y: the page scrolls, and the halo still lights up where the finger lands.--mvm-front-y:center if your layer has no text, or for a button whose layer repeats the label.