|
2 | 2 |
|
3 | 3 | import React, { useState, useEffect } from "react";
|
4 | 4 | import { useTheme } from "next-themes";
|
| 5 | +import { motion, AnimatePresence } from "framer-motion"; |
5 | 6 | import { CardEffects } from "@/components/ui/card-effects";
|
6 | 7 |
|
| 8 | +// Color scheme configurations |
| 9 | +const COLOR_SCHEMES = [ |
| 10 | + { |
| 11 | + id: 1, |
| 12 | + name: 'rose-pink', |
| 13 | + background: "from-rose-300/10 via-pink-300/10 to-purple-300/10", |
| 14 | + text: "from-rose-400 via-pink-400 to-purple-400", |
| 15 | + glow: "rgba(244, 114, 182, 0.15)" |
| 16 | + }, |
| 17 | + { |
| 18 | + id: 2, |
| 19 | + name: 'blue-cyan', |
| 20 | + background: "from-blue-300/10 via-cyan-300/10 to-teal-300/10", |
| 21 | + text: "from-blue-400 via-cyan-400 to-teal-400", |
| 22 | + glow: "rgba(34, 211, 238, 0.15)" |
| 23 | + }, |
| 24 | + { |
| 25 | + id: 3, |
| 26 | + name: 'violet-blue', |
| 27 | + background: "from-violet-300/10 via-indigo-300/10 to-blue-300/10", |
| 28 | + text: "from-violet-400 via-indigo-400 to-blue-400", |
| 29 | + glow: "rgba(129, 140, 248, 0.15)" |
| 30 | + } |
| 31 | +] as const; |
| 32 | + |
| 33 | +// Animation configurations |
| 34 | +const GRADIENT_ANIMATION = { |
| 35 | + initial: { opacity: 0, backgroundPosition: "0% 50%" }, |
| 36 | + animate: { |
| 37 | + opacity: 1, |
| 38 | + backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"] |
| 39 | + }, |
| 40 | + exit: { opacity: 0 }, |
| 41 | + transition: { |
| 42 | + opacity: { duration: 0.8, ease: "easeInOut" }, |
| 43 | + backgroundPosition: { |
| 44 | + duration: 12, |
| 45 | + ease: "linear", |
| 46 | + repeat: Infinity, |
| 47 | + repeatType: "reverse" |
| 48 | + } |
| 49 | + } |
| 50 | +}; |
| 51 | + |
| 52 | +const GRADIENT_STYLE = { |
| 53 | + backgroundSize: "200% 100%", |
| 54 | + transform: "translate3d(0, 0, 0)", |
| 55 | + backfaceVisibility: "hidden" as const, |
| 56 | + WebkitBackfaceVisibility: "hidden" as const, |
| 57 | + willChange: "opacity, background-position", |
| 58 | +}; |
| 59 | + |
| 60 | +// Loading state component |
| 61 | +const LoadingState = () => ( |
| 62 | + <div className="max-w-6xl mx-auto"> |
| 63 | + <CardEffects variant="featured" className="rounded-2xl overflow-hidden"> |
| 64 | + <div className="px-8 md:px-16 py-10 md:py-14 relative"> |
| 65 | + <h1 className="text-center"> |
| 66 | + <span className="block text-2xl md:text-4xl mb-4 text-zinc-700 dark:text-zinc-200"> |
| 67 | + Hello, I am |
| 68 | + </span> |
| 69 | + <span className="block text-4xl md:text-8xl font-bold"> |
| 70 | + <span className="md:hidden">HAMMY</span> |
| 71 | + <span className="hidden md:inline">HAMMAYO</span> |
| 72 | + </span> |
| 73 | + </h1> |
| 74 | + <div className="mt-6 h-1.5 w-40 md:w-64 mx-auto rounded-full" /> |
| 75 | + </div> |
| 76 | + </CardEffects> |
| 77 | + </div> |
| 78 | +); |
| 79 | + |
7 | 80 | export function HeroTitle() {
|
8 |
| - const { theme, resolvedTheme } = useTheme(); |
| 81 | + const { resolvedTheme } = useTheme(); |
9 | 82 | const [mounted, setMounted] = useState(false);
|
10 |
| - const [boxShadow, setBoxShadow] = useState(""); |
| 83 | + const [currentScheme, setCurrentScheme] = useState(0); |
11 | 84 |
|
12 |
| - // Only access theme on the client-side |
13 | 85 | useEffect(() => {
|
14 | 86 | setMounted(true);
|
| 87 | + const interval = setInterval(() => { |
| 88 | + setCurrentScheme((current) => (current + 1) % COLOR_SCHEMES.length); |
| 89 | + }, 5000); |
| 90 | + return () => clearInterval(interval); |
| 91 | + }, []); |
15 | 92 |
|
16 |
| - const currentTheme = resolvedTheme || theme; |
17 |
| - if (currentTheme === 'dark') { |
18 |
| - setBoxShadow('0 0 15px rgba(168, 85, 247, 0.5), 0 0 30px rgba(6, 182, 212, 0.3)'); |
19 |
| - } else { |
20 |
| - setBoxShadow('0 0 15px rgba(168, 85, 247, 0.3), 0 0 30px rgba(6, 182, 212, 0.2)'); |
21 |
| - } |
22 |
| - }, [theme, resolvedTheme]); |
| 93 | + if (!mounted) return <LoadingState />; |
| 94 | + |
| 95 | + const scheme = COLOR_SCHEMES[currentScheme]; |
| 96 | + const glowOpacity = resolvedTheme === 'dark' ? '0.3' : '0.15'; |
| 97 | + const currentGlow = scheme.glow.replace('0.15', glowOpacity); |
23 | 98 |
|
24 | 99 | return (
|
25 |
| - <div className="relative mx-auto max-w-6xl p-6 rounded-2xl backdrop-blur-lg border dark:border-zinc-800/30 border-zinc-200/30 dark:bg-black/10 bg-white/10 group hover:shadow-lg hover:shadow-purple-500/10 transition-all duration-500"> |
26 |
| - <div className="absolute inset-0 bg-gradient-to-br opacity-0 from-purple-500/10 via-cyan-500/5 transition-opacity duration-700 group-hover:opacity-100 rounded-2xl -z-10" /> |
27 |
| - <div className="max-w-6xl mx-auto"> |
28 |
| - <CardEffects variant="featured" className="rounded-2xl"> |
29 |
| - <div className="px-16 py-10 md:py-14"> |
30 |
| - <h1 className="text-5xl md:text-8xl font-bold tracking-tight letter-spacing-mono-normal"> |
31 |
| - <span className="block leading-none text-zinc-800 dark:text-zinc-200"> |
32 |
| - <span className="text-4xl md:text-8xl">Hello, I am</span> |
33 |
| - </span> |
34 |
| - <span className="block text-transparent bg-clip-text bg-gradient-to-r from-purple-500 via-cyan-500 to-green-500 leading-tight opacity-90 group-hover:opacity-100 transition-opacity duration-500"> |
35 |
| - <span className="text-4xl md:hidden">HAMMY</span> |
36 |
| - <span className="hidden md:inline">HAMMAYO</span> |
37 |
| - </span> |
38 |
| - </h1> |
39 |
| - <div |
40 |
| - className="mt-4 h-1.5 w-40 md:w-64 mx-auto rounded-full bg-gradient-to-r from-purple-500 via-cyan-500 to-green-500 opacity-90 group-hover:opacity-100 transition-opacity duration-500" |
41 |
| - style={mounted ? { boxShadow } : {}} |
| 100 | + <div className="max-w-6xl mx-auto"> |
| 101 | + <CardEffects variant="featured" className="rounded-2xl overflow-hidden"> |
| 102 | + <div className="absolute inset-0"> |
| 103 | + <AnimatePresence mode="wait"> |
| 104 | + <motion.div |
| 105 | + key={scheme.id} |
| 106 | + className={`absolute inset-0 bg-gradient-to-r ${scheme.background}`} |
| 107 | + {...GRADIENT_ANIMATION} |
| 108 | + style={GRADIENT_STYLE} |
42 | 109 | />
|
43 |
| - </div> |
44 |
| - </CardEffects> |
45 |
| - </div> |
| 110 | + </AnimatePresence> |
| 111 | + </div> |
| 112 | + |
| 113 | + <div className="px-8 md:px-16 py-10 md:py-14 relative"> |
| 114 | + <h1 className="text-center"> |
| 115 | + <span className="block text-2xl md:text-4xl mb-4 text-zinc-700 dark:text-zinc-200"> |
| 116 | + Hello, I am |
| 117 | + </span> |
| 118 | + <span |
| 119 | + className={`block text-4xl md:text-8xl font-bold text-transparent bg-clip-text bg-gradient-to-r ${scheme.text}`} |
| 120 | + > |
| 121 | + <span className="md:hidden">HAMMY</span> |
| 122 | + <span className="hidden md:inline">HAMMAYO</span> |
| 123 | + </span> |
| 124 | + </h1> |
| 125 | + |
| 126 | + <div |
| 127 | + className={`mt-6 h-1.5 w-40 md:w-64 mx-auto rounded-full bg-gradient-to-r ${scheme.text}`} |
| 128 | + style={{ boxShadow: `0 0 15px ${currentGlow}` }} |
| 129 | + /> |
| 130 | + </div> |
| 131 | + </CardEffects> |
46 | 132 | </div>
|
47 | 133 | );
|
48 | 134 | }
|
0 commit comments