Cursor effect
Cursor spotlight effect: a glow that follows the mouse
A soft glow follows the pointer across a dark panel, as if you were holding a torch over it. The text sits above the light, so it stays readable.
Usage
When to use it
- Dark hero sections and feature panels on product pages.
- Pricing or feature cards, to light up the one under the pointer.
- Teaser pages where a little discovery fits the mood.
- Keep body text above the glow layer, as this snippet does, and check contrast with the glow at its brightest.
Code
Copy the effect
<section class="spotlight">
<h2>Move your mouse over this panel</h2>
<p>A soft light follows the pointer. The text stays above it.</p>
</section> .spotlight {
--spot-x: 50%;
--spot-y: 50%;
position: relative;
overflow: hidden;
padding: 64px 32px;
background: #15161a;
color: #f5f3ec;
}
.spotlight::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(280px circle at var(--spot-x) var(--spot-y), rgba(45, 91, 255, 0.35), transparent 70%);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.spotlight > * {
position: relative;
}
.spotlight.is-lit::before {
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.spotlight::before {
--spot-x: 50%;
--spot-y: 50%;
opacity: 1;
transition: none;
}
} (() => {
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
document.querySelectorAll('.spotlight').forEach((panel) => {
let frame = 0;
let x = 0;
let y = 0;
panel.addEventListener('pointermove', (event) => {
if (reduceMotion.matches || event.pointerType === 'touch') return;
const rect = panel.getBoundingClientRect();
x = event.clientX - rect.left;
y = event.clientY - rect.top;
panel.classList.add('is-lit');
if (frame) return;
frame = requestAnimationFrame(() => {
panel.style.setProperty('--spot-x', `${x}px`);
panel.style.setProperty('--spot-y', `${y}px`);
frame = 0;
});
});
panel.addEventListener('pointerleave', () => panel.classList.remove('is-lit'));
});
})(); Accessibility
Motion, touch and screen readers
Reduced motion
With prefers-reduced-motion: reduce, the CSS pins the glow to the centre of the panel and shows it at full strength with no fade, and the script stops updating the position. Visitors get a still, evenly lit panel instead of a moving light.
Accessibility notes
- The glow is a CSS background on a pseudo-element, so screen readers never see it and it cannot block clicks.
- Text and buttons sit above the glow (position: relative on the panel's children). Check contrast at the glow's brightest point.
- The native cursor is untouched.
- Touch screens never see the moving light; the panel simply stays dark until it is hovered with a mouse.
Site builders
Webflow, Framer and React
Webflow
In Webflow, put the CSS inside a <style> tag and the JavaScript inside a <script> tag in the page's custom code (Before </body> tag), or site-wide in Site settings > Custom code. Give your section the class spotlight in the Designer. The ::before glow lives in the CSS, so no extra elements are needed.
Framer
Add the class through an Embed wrapper, or rebuild the panel as a code component that renders the section and runs the script in useEffect. Framer's built-in effects do not expose the pointer position as a CSS variable.
React
Use a ref on the section and attach the pointermove and pointerleave listeners in useEffect, or use React's onPointerMove prop and set the CSS variables with ref.current.style.setProperty. Avoid storing x and y in state; updating a CSS variable skips a re-render.
FAQ
Questions
- Can I use the spotlight on light backgrounds?
- Yes, but a light glow on a light panel is hard to see. Use a darker or more saturated colour, or switch the gradient to darken instead of lighten.
- How do I make the light bigger?
- Change 280px in the radial-gradient to the radius you want.
- Can several panels each have their own spotlight?
- Yes. The script sets up every element with the class spotlight, and each keeps its own position.