Spotlight Hover

스포트라이트 호버

A circle of light follows the cursor across a dark surface, like a torch — only the area under it is revealed.

Also known as: Spotlight cursorTorch effect
···
html
<div class="spot" id="sp"><span>Spotlight</span></div>
css
.fx-dot{position:absolute;left:0;top:0;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;
  background:var(--accent);box-shadow:0 0 0 5px color-mix(in srgb,var(--accent) 18%,transparent),0 2px 6px rgba(0,0,0,.3);
  pointer-events:none;z-index:9999;transition:opacity .25s}
.spot{position:absolute;inset:0;display:grid;place-items:center;overflow:hidden;background:#0d0d14}
.spot::before{content:"";position:absolute;inset:0;background:radial-gradient(220px circle at var(--x,50%) var(--y,50%),
  color-mix(in srgb,var(--accent) 38%,transparent), transparent 62%)}
.spot span{position:relative;color:#f1f0ec;font-weight:700;font-size:clamp(16px,5vmin,22px);letter-spacing:-.01em}
js
const fxDot=document.createElement('div');fxDot.className='fx-dot';document.body.appendChild(fxDot);
let fxAuto=true,fxX=innerWidth/2,fxY=innerHeight/2;
function fxHide(){fxDot.style.opacity='0';}
addEventListener('pointerdown',()=>{fxAuto=false;fxHide();},{capture:true});
addEventListener('pointermove',e=>{fxAuto=false;fxHide();fxX=e.clientX;fxY=e.clientY;},{capture:true});
function fxWalk(x,y,ms){
  return new Promise(res=>{
    const sx=fxX,sy=fxY,start=performance.now();
    function step(now){
      if(!fxAuto)return res();
      const p=Math.min(1,(now-start)/ms),e=1-Math.pow(1-p,3);
      fxX=sx+(x-sx)*e;fxY=sy+(y-sy)*e;
      fxDot.style.transform='translate('+fxX+'px,'+fxY+'px)';
      if(p<1)requestAnimationFrame(step);else res();
    }
    requestAnimationFrame(step);
  });
}
function fxWait(ms){
  return new Promise(res=>{
    const start=performance.now();
    function step(now){ if(!fxAuto)return res(); if(now-start<ms)requestAnimationFrame(step);else res(); }
    requestAnimationFrame(step);
  });
}

const spot=document.getElementById('sp');
function render(x,y){
  const r=spot.getBoundingClientRect();
  spot.style.setProperty('--x',(x-r.left)+'px');
  spot.style.setProperty('--y',(y-r.top)+'px');
}
let t=0;
(function loop(){
  if(fxAuto){
    t+=0.018;
    fxX=innerWidth/2+Math.sin(t)*innerWidth*0.4;
    fxY=innerHeight/2+Math.cos(t*1.4)*innerHeight*0.35;
    fxDot.style.transform='translate('+fxX+'px,'+fxY+'px)';
  }
  render(fxX,fxY);
  requestAnimationFrame(loop);
})();

Just move a radial-gradient to the cursor's coordinates using `background-position` or a CSS custom property. The background itself stays dark, and a bright circle 200–300px wide follows the pointer across it.

In a card grid, add this per-card and fade it out (opacity transition) once the cursor leaves — it naturally highlights "the card you're currently looking at." Add `mix-blend-mode: overlay` and the light blends into the background color for a softer look.

Too small a radius feels like hunting around and gets tiring; too large and you can't tell the effect is there at all. Around 20–35% of the content width is a comfortable range.

When to use

Use it on dark-theme card grids or feature sections where you want to subtly highlight "what you're looking at right now."