aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
92 lines (89 loc) • 2.77 kB
JavaScript
'use client';
import { jsx } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import { forwardRef, useRef, useEffect } from 'react';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { useA11yId } from '../../utils/a11y.js';
/**
* CursorGlow
* Renders a pointer-following radial glow. Pointer-events: none.
* Uses rAF to throttle updates. Respects reduced motion.
*/
const CursorGlow = /*#__PURE__*/forwardRef(({
size = 280,
intensity = 0.6,
color = 'var(--glass-white)',
opacity = 0.18,
global = true,
respectMotionPreference = true,
className,
'aria-label': ariaLabel,
id,
...props
}, ref) => {
const reduce = useReducedMotion();
const componentId = useA11yId(id || 'cursor-glow');
const localRef = useRef(null);
const frame = useRef(null);
const latest = useRef({
x: -9999,
y: -9999
});
// Combine refs
const combinedRef = node => {
localRef.current = node;
if (typeof ref === 'function') {
ref(node);
} else if (ref) {
ref.current = node;
}
};
useEffect(() => {
if (respectMotionPreference && reduce) return; // respect reduced motion
const handleMove = e => {
latest.current = {
x: e.clientX,
y: e.clientY
};
if (frame.current) return;
frame.current = requestAnimationFrame(() => {
frame.current = null;
const el = localRef.current;
if (!el) return;
const {
x,
y
} = latest.current;
el.style.background = `radial-gradient(${size}px ${size}px at ${x}px ${y}px, ${color}${toAlpha(opacity)}, transparent ${Math.round(100 * (1 - opacity))}%)`;
});
};
window.addEventListener('pointermove', handleMove, {
passive: true
});
return () => {
window.removeEventListener('pointermove', handleMove);
if (frame.current) cancelAnimationFrame(frame.current);
};
}, [size, color, opacity, reduce, respectMotionPreference]);
return jsx("div", {
ref: combinedRef,
id: componentId,
className: cn(global ? 'glass-fixed' : 'glass-absolute', 'glass-inset-0 glass-pointer-events-none glass-z-2', className),
"aria-hidden": !ariaLabel,
"aria-label": ariaLabel,
role: ariaLabel ? "img" : undefined,
style: {
mixBlendMode: 'screen',
filter: `blur(${Math.round(size / 16)}px) saturate(${100 + intensity * 60}%)`
},
...props
});
});
function toAlpha(opacity) {
const a = Math.max(0, Math.min(1, opacity));
const hex = Math.round(a * 255).toString(16).padStart(2, '0');
return hex.length === 2 ? hex : '2d';
}
CursorGlow.displayName = 'CursorGlow';
export { CursorGlow, CursorGlow as default };
//# sourceMappingURL=CursorGlow.js.map