스포트라이트 호버

Spotlight Hover

어두운 배경 위에서 커서를 손전등처럼 따라다니는 원형 빛무리가 그 주변만 밝게 드러내는 효과.

다른 이름: 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);
})();

radial-gradient를 커서 좌표에 맞춰 `background-position` 또는 CSS 커스텀 프로퍼티로 이동시키기만 하면 된다. 배경 자체가 어둡고, 그 위에 반경 200~300px짜리 밝은 원이 커서를 따라오는 구조다.

카드 그리드에서는 카드마다 이 효과를 넣고, 카드 밖으로 나가면 서서히 꺼지게(opacity transition) 만들면 "지금 보고 있는 카드"가 자연스럽게 강조된다. `mix-blend-mode: overlay`를 곁들이면 빛이 배경 색과 섞여 더 은은해진다.

원의 반경이 너무 작으면 찾아다니는 느낌이 강해 피곤하고, 너무 크면 효과가 있는지도 모른다. 보통 콘텐츠 폭의 20~35% 정도가 적당하다.

언제 쓰나

다크 테마 카드 그리드, 기능 소개 섹션처럼 "지금 주목하는 항목"을 은근히 강조할 때 씁니다.