Cards✨ Premium

Spotlight Effect

A ::before pseudo-element tracked by the cursor via two CSS properties creates a glowing halo on the card — radial gradient, opacity transition, zero canvas.

JSLightHover

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Cards has 43 effects, including 6 free. Explore the category →

3 usage examples

Pricing card — Highlighting the hovered plan — Spotlight Effect example 1

① Pricing card — Highlighting the hovered plan

WhenA pricing page shows multiple plans side by side on a dark background; the user compares offers.
WhyThe indigo spotlight draws the eye to the hovered plan and reinforces the premium feel without extra visual badges or aggressive animation.
SettingsColor rgba(99, 102, 241, .5) (indigo Pro plan), size 280 px, falloff 70%. Background --bg-card: #12121a.
Feature grid — Spotlighting a product capability — Spotlight Effect example 2

② Feature grid — Spotlighting a product capability

WhenA landing page presents 3–6 key features in a dark card grid; the user explores.
WhyEach hover lights up only the card being pointed at — contrast with unlit neighbors guides attention more effectively than a plain border or shadow change.
SettingsColor rgba(56, 189, 248, .45) (blue-cyan), size 240 px, falloff 65%. All cards share the same CSS; a single querySelectorAll loop wires the listener.
Portfolio gallery — Stage lighting on project cards — Spotlight Effect example 3

③ Portfolio gallery — Stage lighting on project cards

WhenA project or article grid on a very dark background — agency portfolio, template library, creative gallery.
WhyThe amber spot simulates directional stage lighting, adding depth without loading the cards with static decorations.
SettingsColor rgba(251, 191, 36, .4) (warm amber), size 300 px to cover wider thumbnails, falloff 60% for a softer halo.

How it works

The core is the .card-spotlight::before pseudo-element, 200 × 200 px, positioned absolute. Its left: var(--spotlight-x, 50%) and top: var(--spotlight-y, 50%) paired with transform: translate(-50%, -50%) center it exactly on the cursor. Its fill is a radial gradient: radial-gradient(circle, rgba(99,102,241,.5) 0, transparent 70%) — saturated indigo at center, fully transparent at 70% of the radius. The card carries overflow: hidden to clip the halo at its edges.

Position updates are synchronous and direct: on every mousemove event, JS calls getBoundingClientRect() on the card to convert viewport coordinates (clientX, clientY) into card-relative coordinates, then pushes them with style.setProperty('--spotlight-x', …) and style.setProperty('--spotlight-y', …). No requestAnimationFrame needed — the update tracks the cursor pixel by pixel with no lag.

Appearance and disappearance are handled by :hover and an opacity transition: opacity: 0 by default, opacity: 1 on .card-spotlight:hover::before, with transition: opacity .3s. Without this fade, the spot would snap into view abruptly on pointer entry even if the CSS variables are already at the right position.

Accessibility

  • prefers-reduced-motion not handled in this code — the 0.3 s opacity transition plays regardless of system preferences. Recommended fix: @media (prefers-reduced-motion: reduce) { .card-spotlight::before { transition: none; } }.
  • The effect is purely decorative: it neither modifies content nor the semantic structure of the card — no impact on screen readers.
  • :hover-only activation, not :focus. For keyboard users, add .card-spotlight:focus-within::before { opacity: 1; }.
  • Content contrast: background #12121a with white text — ratio above 10:1, meets WCAG AA and AAA.
  • No continuous motion: no RAF, no looping CSS animation — the effect is only active during actual cursor movements.

Browser compatibility

Requires CSS custom properties (var()) and pseudo-elements — available in all modern browsers. Zero external dependencies.

Chrome 90+✓ Full
Firefox 88+✓ Full
Safari 14+✓ Full
Edge 90+✓ Full
Mobile iOS✓ No hover — spot hidden
Android Chrome✓ No hover — spot hidden

Without CSS custom property support, the spot stays fixed at the card center (fallback value 50% declared in CSS) — a static highlight, no JS errors.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="card-demo card-spotlight" id="spotlightCard">
  <h3 class="card-title">Title</h3>
  <p class="card-body">Card content</p>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Spotlight Effect, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Spot color rgba(99, 102, 241, .5) First value in radial-gradient(circle, COLOR 0, transparent 70%) on .card-spotlight::before. All CSS color formats accepted (rgba, hsl, hex).
Spot size (width / height) 200px Dimensions of the ::before element. Increase (300–400 px) for a diffuse halo on large cards; decrease (120 px) for a tighter beam.
Transparency falloff 70% Second parameter of radial-gradient(circle, … 0, transparent 70%). Lower (50%) = harder edge; higher (85%) = very soft halo.
Transition duration 0.3s The transition: opacity .3s property on .card-spotlight::before. 0 s = instant; 0.8 s = slow cinematic fade.
--bg-card #12121a CSS variable for card background. Override inline: style="--bg-card: #1e1b4b".
--radius-xl 1rem Corner radius applied via border-radius: var(--radius-xl) on the card.

FAQ

Not for tracking — on mobile there is no mousemove event and :hover stays inactive: the spot remains hidden. For a touch version, listen to touchmove alongside and call the same style.setProperty() with touch coordinates.
The original JS targets getElementById('spotlightCard') — one element only. For a grid, replace with document.querySelectorAll('.card-spotlight') and loop: els.forEach(el => el.addEventListener('mousemove', e => { const r = el.getBoundingClientRect(); el.style.setProperty('--spotlight-x', (e.clientX-r.left)+'px'); el.style.setProperty('--spotlight-y', (e.clientY-r.top)+'px'); }));
Yes, via a CSS variable. Declare --spot-color: rgba(99,102,241,.5) in .card-spotlight::before and use it in the gradient, then override per state: .card-spotlight.is-danger { --spot-color: rgba(239,68,68,.5); } — color changes without touching the JS.