From e2692431a5d4d118f2b1cc0dbc39e18a7b941722 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:05:52 +0330 Subject: [PATCH] Import source: src/components/Hero Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- src/components/Hero.tsx | 186 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/components/Hero.tsx diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx new file mode 100644 index 0000000..6f172c1 --- /dev/null +++ b/src/components/Hero.tsx @@ -0,0 +1,186 @@ +'use client'; + +import { useEffect, useRef } from 'react'; +import { motion } from 'framer-motion'; +import { useLocale, useTranslations } from 'next-intl'; +import { ArrowDown } from 'lucide-react'; + +export default function Hero() { + const t = useTranslations('Hero'); + const locale = useLocale(); + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + let animationFrameId: number; + let particles: Array<{ + x: number; + y: number; + radius: number; + vx: number; + vy: number; + opacity: number; + }> = []; + + const resizeCanvas = () => { + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; + initParticles(); + }; + + const initParticles = () => { + particles = []; + const density = Math.floor((canvas.width * canvas.height) / 18000); + const count = Math.min(Math.max(density, 15), 50); // Keep it light and subtle + + for (let i = 0; i < count; i++) { + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + radius: Math.random() * 1.5 + 0.5, + vx: (Math.random() - 0.5) * 0.15, + vy: (Math.random() - 0.5) * 0.15, + opacity: Math.random() * 0.35 + 0.1, + }); + } + }; + + const draw = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + const isDark = document.documentElement.classList.contains('dark'); + + // Select subtle colors based on theme: sand or teal + const particleColor = isDark ? '15, 107, 115' : '217, 93, 57'; // Teal for dark theme, terracotta for light theme + + particles.forEach((p) => { + ctx.beginPath(); + ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${particleColor}, ${p.opacity})`; + ctx.fill(); + + // Update positions + p.x += p.vx; + p.y += p.vy; + + // Bounce off bounds + if (p.x < 0 || p.x > canvas.width) p.vx *= -1; + if (p.y < 0 || p.y > canvas.height) p.vy *= -1; + }); + + animationFrameId = requestAnimationFrame(draw); + }; + + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + animationFrameId = requestAnimationFrame(draw); + + return () => { + cancelAnimationFrame(animationFrameId); + window.removeEventListener('resize', resizeCanvas); + }; + }, []); + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + delayChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { duration: 0.8, ease: [0.16, 1, 0.3, 1] as const }, + }, + }; + + return ( +
+ {/* Subtle particle canvas */} + + + {/* Decorative gradient overlay */} +
+ +
+ + {/* Main Typographic Treatment */} + + {locale === 'fa' ? 'آرسام صباغ' : 'Arsam Sabbagh'} + + + {/* Editorial Tagline */} + + {t('tagline')} + + + {/* Action CTAs */} + + + {t('ctaWork')} + + + + {t('ctaWriting')} + + + + + {/* Scroll Indicator */} + { + document.getElementById('about')?.scrollIntoView({ behavior: 'smooth' }); + }} + > + + {t('scroll')} + + + + + +
+
+ ); +}