Files
ArtSplash/app/components/Hero.tsx

199 lines
6.8 KiB
TypeScript

"use client";
import { useEffect, useRef, useCallback } from "react";
import Link from "next/link";
import gsap from "gsap";
const GRID_COLS = 20;
const GRID_ROWS = 12;
function TileGrid() {
const gridRef = useRef<HTMLDivElement>(null);
const tilesRef = useRef<(HTMLDivElement | null)[]>([]);
const handleMouseEnter = useCallback((index: number) => {
const tile = tilesRef.current[index];
if (tile) {
gsap.killTweensOf(tile);
gsap.to(tile, {
rotateY: 180,
duration: 0.5,
ease: "power2.inOut",
overwrite: true,
});
}
}, []);
const handleMouseLeave = useCallback((index: number) => {
const tile = tilesRef.current[index];
if (tile) {
gsap.killTweensOf(tile);
gsap.to(tile, {
rotateY: 0,
duration: 0.5,
ease: "power2.inOut",
overwrite: true,
});
}
}, []);
return (
<div
ref={gridRef}
className="absolute inset-0 grid z-0"
style={{
gridTemplateColumns: `repeat(${GRID_COLS}, 1fr)`,
gridTemplateRows: `repeat(${GRID_ROWS}, 1fr)`,
}}
>
{Array.from({ length: GRID_COLS * GRID_ROWS }).map((_, index) => (
<div
key={index}
onMouseEnter={() => handleMouseEnter(index)}
onMouseLeave={() => handleMouseLeave(index)}
className="cursor-pointer"
style={{ perspective: "500px" }}
>
<div
ref={(el) => { tilesRef.current[index] = el; }}
className="w-full h-full relative"
style={{
transformStyle: "preserve-3d",
willChange: "transform",
}}
>
{/* Front - Black */}
<div
className="absolute inset-0 bg-black border border-zinc-900"
style={{ backfaceVisibility: "hidden" }}
/>
{/* Back - White */}
<div
className="absolute inset-0 bg-white"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
/>
</div>
</div>
))}
</div>
);
}
export default function Hero() {
const headingRef = useRef<HTMLHeadingElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Animate heading on mount
if (headingRef.current) {
gsap.fromTo(
headingRef.current,
{ opacity: 0, y: 50 },
{ opacity: 1, y: 0, duration: 1, ease: "power3.out", delay: 0.3 }
);
}
if (contentRef.current) {
gsap.fromTo(
contentRef.current.children,
{ opacity: 0, y: 30 },
{ opacity: 1, y: 0, duration: 0.8, stagger: 0.15, ease: "power3.out", delay: 0.5 }
);
}
}, []);
return (
<section className="relative min-h-screen bg-black overflow-hidden pt-28">
{/* Tile Grid Background */}
<TileGrid />
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-full pointer-events-none">
<div className="flex items-center justify-center min-h-[calc(100vh-12rem)] pb-20">
{/* Center Content */}
<div className="text-center relative z-10">
<div className="inline-flex items-center gap-2 bg-lime-400 text-black px-5 py-2.5 text-xs font-bold tracking-widest rounded-full mb-10 pointer-events-auto">
<span className="w-2 h-2 bg-black rounded-full animate-pulse" />
FEBRUARY 2026
</div>
<h1
ref={headingRef}
className="text-6xl sm:text-7xl lg:text-8xl xl:text-9xl font-black tracking-tighter text-white leading-[0.85] mb-8"
style={{
WebkitTextStroke: "2px black",
paintOrder: "stroke fill",
}}
>
<span className="block">UNLEASH</span>
<span
className="block text-lime-400 drop-shadow-[0_0_30px_rgba(163,230,53,0.5)]"
style={{
WebkitTextStroke: "2px black",
paintOrder: "stroke fill",
}}
>
YOUR ART
</span>
</h1>
<div ref={contentRef}>
<p className="text-zinc-300 text-base sm:text-lg max-w-lg mx-auto mb-10 leading-relaxed font-light drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)]">
From canvas to screen, showcase your creativity and let your
artwork speak to the world.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center pointer-events-auto mb-12">
<Link
href="/register"
className="inline-flex items-center justify-center gap-2 bg-lime-400 text-black px-10 py-4 text-sm font-bold tracking-wider hover:bg-lime-300 transition-all hover:scale-105 rounded-full shadow-[0_0_30px_rgba(163,230,53,0.3)]"
>
JOIN COMPETITION
</Link>
<Link
href="/gallery"
className="inline-flex items-center justify-center gap-2 bg-transparent text-white px-10 py-4 text-sm font-bold tracking-wider hover:bg-white/10 transition-all rounded-full border border-zinc-700 hover:border-zinc-500"
>
EXPLORE GALLERY
</Link>
</div>
{/* Scroll Indicator */}
<div className="flex flex-col items-center gap-2">
<span className="text-lime-400 text-[10px] tracking-[0.3em] uppercase drop-shadow-[0_0_10px_rgba(163,230,53,0.8)]">Scroll</span>
<div className="w-px h-8 bg-gradient-to-b from-lime-400 to-transparent shadow-[0_0_10px_rgba(163,230,53,0.5)]" />
</div>
</div>
</div>
</div>
</div>
{/* Bottom Marquee */}
<div className="absolute bottom-0 left-0 right-0 bg-lime-400 text-black py-3 overflow-hidden z-10">
<div className="animate-marquee-slow whitespace-nowrap flex items-center">
{Array(8)
.fill(null)
.map((_, i) => (
<div key={i} className="flex items-center">
<span className="text-xl sm:text-2xl font-black tracking-tight mx-6 uppercase">
ArtSplash
</span>
<span className="text-black/40 mx-4"></span>
<span className="text-xl sm:text-2xl font-black tracking-tight mx-6 uppercase text-black/60">
SJEC
</span>
<span className="text-black/40 mx-4"></span>
<span className="text-xl sm:text-2xl font-black tracking-tight mx-6 uppercase">
2026
</span>
<span className="text-black/40 mx-4"></span>
</div>
))}
</div>
</div>
</section>
);
}