Files
yslootahrobotics/src/components/ui/CursorSpotlight.tsx
T
NajjarandClaude Opus 4.8 873026a4a9
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions
feat: monochrome black & white rebrand + new Pudu robots
- Convert site from royal-blue theme to black/white monochrome (desaturate
  all blue-hued colors; keep WhatsApp green and error red)
- Swap blue circular brand logo for mono YS mark; regenerate favicon/PWA icons
- Add 6 Pudu robots (CC1 Pro, MT1 Max, MT1 Vac, T600, T600 Underride, BG1 Pro)
- Fix favicon.ico to RGBA (Next 16 Turbopack decode) and Stripe apiVersion type

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 13:20:26 +04:00

63 lines
1.7 KiB
TypeScript

'use client';
import { useEffect, useRef, useState } from 'react';
type Props = {
color?: string;
size?: number;
className?: string;
};
export function CursorSpotlight({ color = 'rgba(94, 94, 94, 0.18)', size = 520, className = '' }: Props) {
const ref = useRef<HTMLDivElement | null>(null);
const [pos, setPos] = useState<{ x: number; y: number } | null>(null);
const [enabled, setEnabled] = useState(false);
useEffect(() => {
const finePointer = window.matchMedia('(pointer: fine)').matches;
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!finePointer || reduced) return;
setEnabled(true);
const el = ref.current?.parentElement;
if (!el) return;
let raf = 0;
const onMove = (e: MouseEvent) => {
const rect = el.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
cancelAnimationFrame(raf);
raf = requestAnimationFrame(() => setPos({ x, y }));
};
const onLeave = () => setPos(null);
el.addEventListener('mousemove', onMove);
el.addEventListener('mouseleave', onLeave);
return () => {
el.removeEventListener('mousemove', onMove);
el.removeEventListener('mouseleave', onLeave);
cancelAnimationFrame(raf);
};
}, []);
if (!enabled) return null;
return (
<div
ref={ref}
aria-hidden
className={className}
style={{
position: 'absolute',
inset: 0,
pointerEvents: 'none',
opacity: pos ? 1 : 0,
transition: 'opacity 0.4s ease',
background: pos
? `radial-gradient(${size}px ${size}px at ${pos.x}px ${pos.y}px, ${color}, transparent 70%)`
: 'transparent',
zIndex: 1,
}}
/>
);
}