Skip to content

Commit 3cf779d

Browse files
committed
feat(ui): add mobile navigation menu and refine homepage animations
- Introduce HamburgerMenu and MobileMenu components for responsive navigation - Update Navigation component to support mobile menu with state management - Enhance homepage with improved scroll animations and portfolio link button - Refine header styling across About, Blog, and Contact pages with gradient text - Adjust layout padding and animation margins for better user experience
1 parent fba46ab commit 3cf779d

10 files changed

Lines changed: 370 additions & 102 deletions

File tree

pnpm-workspace.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
onlyBuiltDependencies:
2+
- '@tailwindcss/oxide'
23
- '@tsparticles/engine'
4+
- esbuild
35
- react-particles
6+
- sharp
47
- tsparticles-engine
8+
- unrs-resolver
9+
- workerd

src/app/about/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function About() {
1515
transition={{ duration: 0.6, ease: "easeOut" }}
1616
className="text-center"
1717
>
18-
<h1 className="text-4xl font-bold mb-8">About</h1>
18+
<h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-neon-cyan to-organic-green bg-clip-text text-transparent">About</h1>
1919
<p className="text-lg">Learn more about the creator</p>
2020
</motion.div>
2121
</div>

src/app/blog/BlogContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default function BlogContent({ posts }: BlogContentProps) {
3131
transition={{ duration: 0.6, ease: "easeOut" }}
3232
className="text-center mb-12"
3333
>
34-
<h1 className="text-4xl font-bold mb-8 font-geist-sans">Blog</h1>
34+
<h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-neon-cyan to-organic-green bg-clip-text text-transparent">Blog</h1>
3535
<p className="text-lg text-organic-green">Latest thoughts and insights from the cosmic frontier</p>
3636
</motion.div>
3737

src/app/contact/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function Contact() {
1515
transition={{ duration: 0.6, ease: "easeOut" }}
1616
className="text-center"
1717
>
18-
<h1 className="text-4xl font-bold mb-8">Contact</h1>
18+
<h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-neon-cyan to-organic-green bg-clip-text text-transparent">Contact</h1>
1919
<p className="text-lg">Get in touch</p>
2020
</motion.div>
2121
</div>

src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default function RootLayout({
3030
return (
3131
<html lang="en">
3232
<body
33-
className={`${geistSans.variable} ${geistMono.variable} antialiased relative overflow-x-hidden`}
33+
className={`${geistSans.variable} ${geistMono.variable} antialiased relative overflow-x-hidden pt-16`}
3434
>
3535
<BackgroundWrapper />
3636
<Navigation />

src/app/page.tsx

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import Link from "next/link";
34
import { motion } from "framer-motion";
45
import {
56
useScrollAnimation,
@@ -11,6 +12,8 @@ import {
1112
} from "../lib/animations";
1213
//import WeatherWidget from "../components/WeatherWidget";
1314
import dynamic from "next/dynamic";
15+
import CosmicSphere from "../components/CosmicSphere";
16+
import { useEffect, useState } from "react";
1417

1518
// Lazy load Testimonials for better performance
1619
const Testimonials = dynamic(() => import("../components/Testimonials"), {
@@ -23,18 +26,49 @@ const Testimonials = dynamic(() => import("../components/Testimonials"), {
2326
),
2427
});
2528

26-
// Lazy load the 3D cosmic sphere for performance
27-
const CosmicSphere = dynamic(() => import("../components/CosmicSphere"), {
28-
ssr: false,
29-
loading: () => (
30-
<div className="w-64 h-64 bg-cosmic-blue/20 rounded-full animate-pulse" />
31-
),
32-
});
33-
3429
export default function Home() {
3530
const { ref: heroRef, inView: heroInView } = useScrollAnimation();
3631
const { ref: featuresRef, inView: featuresInView } = useScrollAnimation();
3732
const reducedMotion = prefersReducedMotion();
33+
const [hasMounted, setHasMounted] = useState(false);
34+
35+
useEffect(() => {
36+
setHasMounted(true);
37+
}, []);
38+
39+
// Fallback: if component has mounted but inView is still false after 1 second, assume it's visible
40+
const [heroVisible, setHeroVisible] = useState(false);
41+
const [featuresVisible, setFeaturesVisible] = useState(false);
42+
43+
useEffect(() => {
44+
if (hasMounted && !reducedMotion) {
45+
const timer = setTimeout(() => {
46+
if (!heroVisible) {
47+
setHeroVisible(true);
48+
}
49+
}, 1000);
50+
return () => clearTimeout(timer);
51+
}
52+
}, [hasMounted, reducedMotion, heroVisible]);
53+
54+
useEffect(() => {
55+
if (hasMounted && !reducedMotion) {
56+
const timer = setTimeout(() => {
57+
if (!featuresVisible) {
58+
setFeaturesVisible(true);
59+
}
60+
}, 1500);
61+
return () => clearTimeout(timer);
62+
}
63+
}, [hasMounted, reducedMotion, featuresVisible]);
64+
65+
useEffect(() => {
66+
if (heroInView) setHeroVisible(true);
67+
}, [heroInView]);
68+
69+
useEffect(() => {
70+
if (featuresInView) setFeaturesVisible(true);
71+
}, [featuresInView]);
3872

3973
const features = [
4074
{
@@ -64,7 +98,7 @@ export default function Home() {
6498
];
6599

66100
return (
67-
<div className="min-h-screen bg-cosmic-blue text-neon-cyan pt-16">
101+
<div className="min-h-screen bg-cosmic-blue text-neon-cyan">
68102
{/* Hero Section */}
69103
<section
70104
ref={heroRef}
@@ -73,50 +107,51 @@ export default function Home() {
73107
<div className="container mx-auto px-4 text-center">
74108
<motion.div
75109
initial={reducedMotion ? {} : { opacity: 0, y: 50 }}
76-
animate={heroInView && !reducedMotion ? { opacity: 1, y: 0 } : {}}
110+
animate={heroVisible ? { opacity: 1, y: 0 } : {}}
77111
transition={{ duration: 0.8, ease: "easeOut" }}
78112
className="max-w-4xl mx-auto"
79113
>
80114
<motion.h1
81115
initial={reducedMotion ? {} : { opacity: 0, scale: 0.5 }}
82-
animate={
83-
heroInView && !reducedMotion ? { opacity: 1, scale: 1 } : {}
84-
}
116+
animate={heroVisible ? { opacity: 1, scale: 1 } : {}}
85117
transition={{ duration: 0.8, delay: 0.2, ease: "easeOut" }}
86118
className="text-6xl md:text-8xl font-bold mb-6 bg-gradient-to-r from-neon-cyan via-organic-green to-holographic-purple bg-clip-text text-transparent"
87119
>
88120
Cosmic Nexus
89121
</motion.h1>
90122
<motion.p
91123
initial={reducedMotion ? {} : { opacity: 0, y: 30 }}
92-
animate={heroInView && !reducedMotion ? { opacity: 1, y: 0 } : {}}
124+
animate={heroVisible ? { opacity: 1, y: 0 } : {}}
93125
transition={{ duration: 0.8, delay: 0.4, ease: "easeOut" }}
94126
className="text-xl md:text-2xl text-neon-cyan/80 mb-8 max-w-2xl mx-auto"
95127
>
96-
Blending futuristic technology with nature&apos;s wonders to
128+
Blending futuristic technology with nature's wonders to
97129
create extraordinary digital experiences
98130
</motion.p>
99131
<motion.div
100132
initial={reducedMotion ? {} : { opacity: 0, y: 20 }}
101-
animate={heroInView && !reducedMotion ? { opacity: 1, y: 0 } : {}}
133+
animate={heroVisible ? { opacity: 1, y: 0 } : {}}
102134
transition={{ duration: 0.8, delay: 0.6, ease: "easeOut" }}
103-
className="flex flex-col sm:flex-row gap-4 justify-center"
135+
className="flex flex-col sm:flex-row gap-4 justify-center items-center"
104136
>
137+
<Link href="/portfolio">
138+
<motion.button
139+
whileHover={
140+
reducedMotion
141+
? {}
142+
: {
143+
scale: 1.05,
144+
boxShadow: "0 0 25px rgba(0, 255, 255, 0.4)",
145+
}
146+
}
147+
whileTap={reducedMotion ? {} : { scale: 0.95 }}
148+
className="px-8 py-3 h-12 flex items-center bg-gradient-to-r from-neon-cyan to-organic-green text-cosmic-blue font-bold rounded-lg shadow-lg hover:shadow-xl transition-all duration-300 relative z-10 cursor-pointer w-fit"
149+
>
150+
Explore Portfolio
151+
</motion.button>
152+
</Link>
105153
<motion.button
106-
whileHover={
107-
reducedMotion
108-
? {}
109-
: {
110-
scale: 1.05,
111-
boxShadow: "0 0 25px rgba(0, 255, 255, 0.4)",
112-
}
113-
}
114-
whileTap={reducedMotion ? {} : { scale: 0.95 }}
115-
className="px-8 py-3 bg-gradient-to-r from-neon-cyan to-organic-green text-cosmic-blue font-bold rounded-lg shadow-lg hover:shadow-xl transition-all duration-300"
116-
>
117-
Explore Portfolio
118-
</motion.button>
119-
<motion.button
154+
onClick={() => document.getElementById('expertise')?.scrollIntoView({ behavior: 'smooth' })}
120155
whileHover={
121156
reducedMotion
122157
? {}
@@ -126,7 +161,7 @@ export default function Home() {
126161
}
127162
}
128163
whileTap={reducedMotion ? {} : { scale: 0.95 }}
129-
className="px-8 py-3 border-2 border-holographic-purple text-holographic-purple font-bold rounded-lg hover:bg-holographic-purple hover:text-cosmic-blue transition-all duration-300"
164+
className="px-8 py-3 h-12 flex items-center border-2 border-holographic-purple text-holographic-purple font-bold rounded-lg hover:bg-holographic-purple hover:text-cosmic-blue transition-all duration-300 relative z-20 cursor-pointer w-fit"
130165
>
131166
Learn More
132167
</motion.button>
@@ -176,7 +211,7 @@ export default function Home() {
176211
{/* Weather Widget */}
177212
<motion.div
178213
initial={reducedMotion ? {} : { opacity: 0, x: 50 }}
179-
animate={heroInView && !reducedMotion ? { opacity: 1, x: 0 } : {}}
214+
animate={heroVisible ? { opacity: 1, x: 0 } : {}}
180215
transition={{ duration: 0.8, delay: 1 }}
181216
className="absolute top-8 right-8 max-w-xs"
182217
>
@@ -185,10 +220,8 @@ export default function Home() {
185220

186221
{/* 3D Cosmic Sphere */}
187222
<motion.div
188-
initial={reducedMotion ? {} : { opacity: 0, scale: 0.5 }}
189-
animate={
190-
heroInView && !reducedMotion ? { opacity: 0.6, scale: 1 } : {}
191-
}
223+
initial={reducedMotion ? {} : { opacity: 0 }}
224+
animate={reducedMotion ? {} : { opacity: 0.6 }}
192225
transition={{ duration: 1.5, delay: 0.8 }}
193226
className="absolute top-1/4 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64 pointer-events-none"
194227
>
@@ -198,14 +231,15 @@ export default function Home() {
198231

199232
{/* Features Section */}
200233
<section
234+
id="expertise"
201235
ref={featuresRef}
202236
className="py-20 bg-gradient-to-b from-transparent to-cosmic-blue/50"
203237
>
204238
<div className="container mx-auto px-4">
205239
<motion.h2
206240
variants={fadeInUp}
207241
initial={reducedMotion ? {} : "hidden"}
208-
animate={featuresInView && !reducedMotion ? "visible" : {}}
242+
animate={featuresVisible ? "visible" : {}}
209243
className="text-4xl md:text-5xl font-bold text-center mb-16 bg-gradient-to-r from-neon-cyan to-organic-green bg-clip-text text-transparent"
210244
>
211245
Our Expertise
@@ -214,7 +248,7 @@ export default function Home() {
214248
<motion.div
215249
variants={staggerContainer}
216250
initial={reducedMotion ? {} : "hidden"}
217-
animate={featuresInView && !reducedMotion ? "visible" : {}}
251+
animate={featuresVisible ? "visible" : {}}
218252
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"
219253
>
220254
{features.map((feature, index) => (
@@ -233,7 +267,7 @@ export default function Home() {
233267
>
234268
<motion.div
235269
initial={reducedMotion ? {} : { scale: 0 }}
236-
animate={featuresInView && !reducedMotion ? { scale: 1 } : {}}
270+
animate={featuresVisible ? { scale: 1 } : {}}
237271
transition={{
238272
delay: index * 0.1 + 0.5,
239273
duration: 0.5,
@@ -256,4 +290,4 @@ export default function Home() {
256290
<Testimonials />
257291
</div>
258292
);
259-
}
293+
}

src/components/HamburgerMenu.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client';
2+
3+
import { motion } from 'framer-motion';
4+
import { hoverLift, hoverGlow, prefersReducedMotion } from '../lib/animations';
5+
6+
interface HamburgerMenuProps {
7+
onClick?: () => void;
8+
className?: string;
9+
}
10+
11+
export default function HamburgerMenu({ onClick, className = '' }: HamburgerMenuProps) {
12+
const reducedMotion = prefersReducedMotion();
13+
14+
return (
15+
<motion.button
16+
onClick={onClick}
17+
onKeyDown={(e) => {
18+
if (e.key === 'Enter' || e.key === ' ') {
19+
e.preventDefault();
20+
onClick?.();
21+
}
22+
}}
23+
whileHover={reducedMotion ? {} : { ...hoverLift, ...hoverGlow }}
24+
whileTap={reducedMotion ? {} : { scale: 0.95 }}
25+
className={`flex flex-col justify-center items-center w-8 h-8 space-y-1 bg-transparent border-none cursor-pointer ${className}`}
26+
aria-label="Menu"
27+
>
28+
<motion.div
29+
className="w-6 h-0.5 bg-neon-cyan rounded-full"
30+
initial={{ opacity: 1 }}
31+
transition={{ duration: 0.2 }}
32+
/>
33+
<motion.div
34+
className="w-6 h-0.5 bg-neon-cyan rounded-full"
35+
initial={{ opacity: 1 }}
36+
transition={{ duration: 0.2 }}
37+
/>
38+
<motion.div
39+
className="w-6 h-0.5 bg-neon-cyan rounded-full"
40+
initial={{ opacity: 1 }}
41+
transition={{ duration: 0.2 }}
42+
/>
43+
</motion.button>
44+
);
45+
}

0 commit comments

Comments
 (0)