Spotlight Follow
CSS pseudo-element + two custom properties: the 400 px indigo halo tracks the cursor in real time — no canvas, no library.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples



How it works
The effect relies entirely on a ::before pseudo-element attached to the .bg-spotlight container. This pseudo-element carries a radial-gradient(circle, rgba(99,102,241,.3) 0, transparent 70%) on a 400×400 px surface — a translucent indigo circle at 30% opacity that fades to nothing at 70% of its radius, never obscuring the background.
The glow is positioned via two CSS custom properties: --mouse-x and --mouse-y. The ::before declares left: var(--mouse-x, 50%) and top: var(--mouse-y, 50%) — the 50% defaults centre the glow before any mouse movement. The pseudo-element is offset by transform: translate(-50%, -50%) so its centre, not its top-left corner, tracks the cursor.
JavaScript listens for mousemove on the container, computes the cursor position as a percentage of the element's BoundingClientRect ((e.clientX - rect.left) / rect.width * 100), then writes the values via el.style.setProperty('--mouse-x', x + '%'). The browser applies the new custom properties to the ::before immediately — no requestAnimationFrame, no canvas, zero layout shift.
pointer-events: none on the pseudo-element ensures the glow never intercepts clicks or hover events on child elements. The transition: opacity .3s on the ::before is intended to smooth a JS-driven show/hide — it does not animate position (cursor tracking is deliberately instant).
Accessibility
- prefers-reduced-motion: absent from the source code — the glow follows the cursor regardless of the OS setting. For sensitive users, add
@media (prefers-reduced-motion: reduce) { .bg-spotlight::before { display: none; } }. - No
aria-hidden,role, oraria-labelon the container in the base code — addaria-hidden="true"if the spotlight is purely decorative, orrole="img"+ descriptivearia-labelif it serves as the primary backdrop of a section. - Text content placed inside the container (e.g.
.bg-label) remains accessible to screen readers — the CSS glow does not touch the DOM. pointer-events: noneon the pseudo-element prevents the glow from intercepting keyboard focus or clicks on child elements.- On mobile / touchscreen,
mousemoveis not fired on touch: the glow stays at its default position (centre) — graceful visual degradation with no JS errors.
Browser compatibility
Requires CSS Custom Properties and pseudo-elements — supported in all modern browsers. Zero external dependencies, zero Canvas.
On older browsers without CSS Custom Properties support (IE), the <code>::before</code> stays at its default position — the centred glow remains visible, no JS errors.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="bg-spotlight" id="spotlightBg">
<!-- Your content: title, button, image -->
<span class="bg-label">Your title</span>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| rgba(99, 102, 241, .3) | indigo at 30% | Glow colour and intensity — replace R/G/B values and alpha (0.1 = very subtle, 0.5 = strong). Brand blue example: rgba(59,130,246,.35). |
| width / height of ::before | 400 px | Light circle diameter — increase to 600–700 px for a large hero, reduce to 200 px for a card grid. |
| transparent 70% | 70% | Transparent stop of the gradient — 50% = sharp focused glow, 85% = very diffuse and enveloping. |
| transition: opacity .3s | 0.3 s | Opacity fade duration — set to 0 s for instant response, or drive opacity via JS for a mouse-enter fade-in. |
| --mouse-x / --mouse-y (initial value) | 50% | Default glow position before the first mouse move — change to 20% / 80% to shift the resting point. |
| background of .bg-spotlight | #0a0a0f | Container background colour — adapt to your page (#111827, #1e1b4b, transparent when the spotlight overlays an image or video). |
FAQ
getElementById('spotlightBg') — one element only. For multiple instances, replace it with a loop over the class: document.querySelectorAll('.bg-spotlight').forEach(function(el){ el.addEventListener('mousemove', function(e){ var r=el.getBoundingClientRect(); el.style.setProperty('--mouse-x',((e.clientX-r.left)/r.width*100)+'%'); el.style.setProperty('--mouse-y',((e.clientY-r.top)/r.height*100)+'%'); }); }). The CSS already works on multiple .bg-spotlight elements.mousemove is not fired. The glow stays centred at its default position (50% / 50%) — a visually neutral fallback with no errors. For touch interactivity, add a touchmove listener and use e.touches[0].clientX/Y in place of mouse coordinates.radial-gradient(circle, rgba(99,102,241,.3) 0, transparent 70%). Replace 99,102,241 with your brand colour in RGB — e.g. blue 59,130,246, violet 139,92,246, red 239,68,68 — and adjust the alpha (0.3 by default) to control glow intensity.