'use client' import { useEffect, useRef } from 'react' /** * Animated background component with geometric grid pattern and floating particles * Features: CSS grid overlay, subtle particle animation, gradient orbs * * @returns JSX element containing layered background with grid, particles, and gradient orbs * * @example * ```tsx * * ``` */ export function AnimatedBackground() { const canvasRef = useRef(null) useEffect(() => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext('2d') if (!ctx) return /** Set canvas dimensions to match window size */ const setCanvasSize = () => { canvas.width = window.innerWidth canvas.height = window.innerHeight } setCanvasSize() window.addEventListener('resize', setCanvasSize) /** Cached dark mode state to avoid DOM queries every frame */ let isDark = document.documentElement.classList.contains('dark') /** Update cached dark mode state when theme changes */ const themeObserver = new MutationObserver(mutations => { for (const mutation of mutations) { if (mutation.attributeName === 'class') { isDark = document.documentElement.classList.contains('dark') } } }) themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }) /** Particle class for subtle floating dots animation */ class Particle { x: number y: number size: number speedX: number speedY: number opacity: number constructor() { this.x = Math.random() * (canvas?.width ?? 800) this.y = Math.random() * (canvas?.height ?? 600) this.size = Math.random() * 1.5 + 0.5 this.speedX = (Math.random() - 0.5) * 0.2 this.speedY = (Math.random() - 0.5) * 0.2 this.opacity = Math.random() * 0.2 + 0.05 } update() { this.x += this.speedX this.y += this.speedY // Wrap around edges if (this.x > (canvas?.width ?? 800)) this.x = 0 if (this.x < 0) this.x = canvas?.width ?? 800 if (this.y > (canvas?.height ?? 600)) this.y = 0 if (this.y < 0) this.y = canvas?.height ?? 600 } draw() { if (!ctx) return const rgb = isDark ? '255, 255, 255' : '0, 0, 0' ctx.fillStyle = `rgba(${rgb}, ${this.opacity})` ctx.beginPath() ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2) ctx.fill() } } // Create particles - fewer for cleaner look const particles: Particle[] = [] const particleCount = 30 for (let i = 0; i < particleCount; i++) { particles.push(new Particle()) } let animationId: number /** * Animation loop for particle system * @returns void - Updates particles and schedules next frame */ const animate = () => { ctx.clearRect(0, 0, canvas.width, canvas.height) particles.forEach(particle => { particle.update() particle.draw() }) animationId = requestAnimationFrame(animate) } animate() return () => { window.removeEventListener('resize', setCanvasSize) themeObserver.disconnect() cancelAnimationFrame(animationId) } }, []) return ( {/* Geometric grid pattern */} {/* Floating particles canvas */} {/* Gradient orbs - more subtle and refined */} {/* Gradient fade to background */} ) }