Skip to content

Commit 0512983

Browse files
committed
Add auto-moving Galaxy background without mouse parallax
1 parent ad8edd5 commit 0512983

7 files changed

Lines changed: 412 additions & 321 deletions

File tree

src/app/globals.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* Apply styles to html and body */
55
html, body {
66
/* Set dark background color */
7-
background-color: #0F0E0E;
7+
background-color: black;
88

99
/* Enable smooth scrolling for anchor links */
1010
scroll-behavior: smooth;

src/components/Bio.tsx

Lines changed: 54 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,48 @@ import {
1010
FaLaptopCode,
1111
} from "react-icons/fa6";
1212
import { IoLogoJavascript } from "react-icons/io5";
13-
import { RiNextjsFill } from "react-icons/ri";
13+
import { RiNextjsFill, RiTailwindCssFill } from "react-icons/ri";
1414
import { GiChessKing } from "react-icons/gi";
1515
import { TbBrandRedux } from "react-icons/tb";
1616
import { SiTypescript } from "react-icons/si";
17-
import { RiTailwindCssFill } from "react-icons/ri";
17+
import GalaxyBackground from "./GalaxyBackgroundParallax"; // <- our new galaxy background
1818

19+
// ===============================
20+
// Bio Component
21+
// ===============================
1922
export default function Bio() {
20-
// List of skills with icons
23+
// ===== Skills Array =====
24+
// Each skill has a name and an icon component with optional color
2125
const skills = [
2226
{ name: "HTML", icon: <FaHtml5 className="text-orange-400" /> },
2327
{ name: "CSS", icon: <FaCss3Alt className="text-blue-500" /> },
2428
{ name: "JavaScript", icon: <IoLogoJavascript className="text-amber-300" /> },
2529
{ name: "React", icon: <FaReact className="text-blue-900" /> },
26-
{ name: "Next", icon: <RiNextjsFill /> },
30+
{ name: "Next.js", icon: <RiNextjsFill /> },
2731
{ name: "TypeScript", icon: <SiTypescript className="text-blue-500" /> },
2832
{ name: "Zustand", icon: <TbBrandRedux className="text-purple-600" /> },
2933
{ name: "Tailwind", icon: <RiTailwindCssFill className="text-blue-500" /> },
3034
];
3135

32-
// Mounted check for client-only rendering
36+
// ===== Mounted State =====
37+
// Used to ensure animations and motion values only run on the client
3338
const [mounted, setMounted] = useState(false);
3439
useEffect(() => setMounted(true), []);
3540

36-
// Motion values for parallax tilt effect
41+
// ===== Mouse Tilt Animation =====
42+
// motionX & motionY track mouse movement
3743
const motionX = useMotionValue(0);
3844
const motionY = useMotionValue(0);
45+
// rotateY & rotateX transform motion values into rotation degrees
3946
const rotateY = useTransform(motionX, (val) => val);
4047
const rotateX = useTransform(motionY, (val) => -val);
4148

42-
// Track mouse position to update tilt
49+
// ===== Mouse Move Effect =====
50+
// Moves the profile image slightly based on mouse position
4351
useEffect(() => {
4452
if (!mounted) return;
4553
const handleMouseMove = (e: MouseEvent) => {
46-
const x = (e.clientX / window.innerWidth - 0.5) * 30;
54+
const x = (e.clientX / window.innerWidth - 0.5) * 30; // Rotate up to 15 degrees
4755
const y = (e.clientY / window.innerHeight - 0.5) * 30;
4856
motionX.set(x);
4957
motionY.set(y);
@@ -52,59 +60,51 @@ export default function Bio() {
5260
return () => window.removeEventListener("mousemove", handleMouseMove);
5361
}, [mounted, motionX, motionY]);
5462

55-
// Generate floating particles once on client
56-
const [particles, setParticles] = useState<{ top: number; left: number }[]>([]);
57-
useEffect(() => {
58-
if (!mounted) return;
59-
setParticles(
60-
Array.from({ length: 40 }).map(() => ({
61-
top: Math.random() * 100,
62-
left: Math.random() * 100,
63-
}))
64-
);
65-
}, [mounted]);
66-
6763
return (
6864
<div id="about" className="relative min-h-screen font-bricolage overflow-hidden">
69-
{/* Animated background gradient */}
70-
<div className="absolute inset-0 -z-10 animate-bgGradient"></div>
65+
{/* ===== Galaxy Background Component ===== */}
66+
<GalaxyBackground />
7167

72-
{/* Floating particles animation */}
73-
<div className="absolute inset-0 -z-10 pointer-events-none">
74-
{particles.map((p, i) => (
75-
<motion.div
76-
key={i}
77-
className="w-1 h-1 bg-white rounded-full opacity-30 absolute"
78-
style={{ top: `${p.top}%`, left: `${p.left}%` }}
79-
animate={{
80-
y: [`0%`, `${Math.random() * 50 - 25}%`, `0%`],
81-
x: [`0%`, `${Math.random() * 50 - 25}%`, `0%`],
82-
}}
83-
transition={{
84-
duration: Math.random() * 8 + 4,
85-
repeat: Infinity,
86-
repeatType: "mirror",
87-
ease: "easeInOut",
88-
}}
68+
{/* ===============================
69+
Main Content
70+
=============================== */}
71+
<div className="flex flex-col lg:flex-row-reverse items-center justify-center mt-36 px-6 lg:px-0 gap-16">
72+
{/* ===== Profile Image with Glow & Tilt ===== */}
73+
<motion.div
74+
className="relative w-80 h-80 lg:w-96 lg:h-96 perspective z-10"
75+
style={{ rotateX, rotateY }}
76+
animate={{ y: ["0%", "-3%", "0%"] }}
77+
transition={{ duration: 6, repeat: Infinity, repeatType: "mirror", ease: "easeInOut" }}
78+
>
79+
{/* Layered Gradient Glows */}
80+
<div className="absolute inset-0 rounded-2xl blur-3xl opacity-60 bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 animate-glow1"></div>
81+
<div className="absolute inset-0 rounded-2xl blur-2xl opacity-40 bg-gradient-to-r from-purple-400 via-pink-400 to-pink-600 animate-glow2"></div>
82+
<div className="absolute inset-0 rounded-2xl blur-xl opacity-30 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 animate-glow3"></div>
83+
84+
<Image
85+
src="https://i.ibb.co/h5pf15q/image.png"
86+
width={400}
87+
height={400}
88+
alt="Hami Parsa"
89+
className="relative rounded-2xl shadow-2xl border border-gray-800"
8990
/>
90-
))}
91-
</div>
91+
</motion.div>
9292

93-
<div className="flex flex-col lg:flex-row-reverse items-center justify-center mt-36 px-6 lg:px-0 gap-16">
94-
{/* Text section with intro and skills */}
93+
{/* ===== Text Section ===== */}
9594
<div className="text-gray-100 max-w-lg flex flex-col gap-6 z-10 relative">
95+
{/* Name with Gradient & Icon */}
9696
<h1 className="flex items-center gap-2 text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 animate-textGradient">
9797
Im Hami Parsa
9898
<GiChessKing className="text-yellow-400 text-4xl animate-bounce" />
9999
</h1>
100100

101+
{/* Role */}
101102
<h2 className="text-2xl flex items-center gap-2 text-gray-300 hover:text-white transition-colors duration-300">
102103
Front End Developer <FaLaptopCode className="text-indigo-400 text-2xl" />
103104
</h2>
104-
105105
<hr className="border-gray-800" />
106106

107-
{/* Skills grid */}
107+
{/* ===== Skills Grid ===== */}
108108
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
109109
{skills.map((skill) => (
110110
<div
@@ -117,76 +117,27 @@ export default function Bio() {
117117
))}
118118
</div>
119119
</div>
120-
121-
{/* Profile image with parallax tilt and glowing layers */}
122-
<motion.div
123-
className="relative w-80 h-80 lg:w-96 lg:h-96 perspective z-10"
124-
style={{ rotateX, rotateY }}
125-
animate={{ y: ["0%", "-3%", "0%"] }}
126-
transition={{ duration: 6, repeat: Infinity, repeatType: "mirror", ease: "easeInOut" }}
127-
>
128-
{/* Neon glow layers */}
129-
<div className="absolute inset-0 rounded-2xl blur-3xl opacity-60 bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 animate-glow1"></div>
130-
<div className="absolute inset-0 rounded-2xl blur-2xl opacity-40 bg-gradient-to-r from-purple-400 via-pink-400 to-pink-600 animate-glow2"></div>
131-
<div className="absolute inset-0 rounded-2xl blur-xl opacity-30 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 animate-glow3"></div>
132-
133-
<Image
134-
src="https://i.ibb.co/h5pf15q/image.png"
135-
width={400}
136-
height={400}
137-
alt="Hami Parsa"
138-
className="relative rounded-2xl shadow-2xl border border-gray-800"
139-
/>
140-
</motion.div>
141120
</div>
142121

143-
{/* Bio paragraph */}
122+
{/* ===== Bio Paragraph ===== */}
144123
<p className="text-white text-center mt-[300px] max-w-3xl mx-auto text-lg sm:text-xl leading-relaxed">
145-
Hi, Im <span className="font-bold text-pink-500">Hami Parsa</span> — a <span className="text-indigo-400 font-semibold">passionate Front-End Developer</span> dedicated to crafting <span className="bg-clip-text text-transparent bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 font-bold">modern, interactive, and visually stunning</span> web experiences.
146-
<br /><br />
147-
I transform ideas into <span className="text-purple-400 font-semibold">user-friendly websites</span> using <span className="text-yellow-400 font-bold">HTML, CSS, JavaScript</span>, <span className="text-blue-500 font-bold">React</span>, and <span className="text-indigo-400 font-bold">Next.js</span>.
148-
<br /><br />
149-
Always learning, always improving — I strive to build projects that combine <span className="text-pink-400 font-semibold">beautiful design</span> with <span className="text-purple-500 font-semibold">high performance</span>.
150-
<br /><br />
151-
Lets collaborate and create <span className="bg-clip-text text-transparent bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 font-bold">something extraordinary</span> together!
124+
Hi, Im <span className="font-bold text-pink-500">Hami Parsa</span> — a <span className="text-indigo-400 font-semibold">passionate Front-End Developer</span> crafting <span className="bg-clip-text text-transparent bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 font-bold">modern, interactive, and visually stunning</span> web experiences.
152125
</p>
153126

154-
{/* Custom animations and styles */}
127+
{/* ===== Custom Animations ===== */}
155128
<style jsx>{`
156129
.perspective { perspective: 1000px; }
157130
158-
@keyframes glow1 {
159-
0% { background-position: 0% 50%; }
160-
50% { background-position: 100% 50%; }
161-
100% { background-position: 0% 50%; }
162-
}
163-
@keyframes glow2 {
164-
0% { background-position: 100% 0%; }
165-
50% { background-position: 0% 100%; }
166-
100% { background-position: 100% 0%; }
167-
}
168-
@keyframes glow3 {
169-
0% { background-position: 50% 0%; }
170-
50% { background-position: 50% 100%; }
171-
100% { background-position: 50% 0%; }
172-
}
173-
131+
/* Glow Animations for Profile Image */
132+
@keyframes glow1 { 0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%} }
133+
@keyframes glow2 { 0%{background-position:100% 0%}50%{background-position:0% 100%}100%{background-position:100% 0%} }
134+
@keyframes glow3 { 0%{background-position:50% 0%}50%{background-position:50% 100%}100%{background-position:50% 0%} }
174135
.animate-glow1 { background-size: 200% 200%; animation: glow1 6s ease-in-out infinite; }
175136
.animate-glow2 { background-size: 200% 200%; animation: glow2 8s ease-in-out infinite; }
176137
.animate-glow3 { background-size: 200% 200%; animation: glow3 10s ease-in-out infinite; }
177138
178-
@keyframes bgGradient {
179-
0% { background-position: 0% 50%; }
180-
50% { background-position: 100% 50%; }
181-
100% { background-position: 0% 50%; }
182-
}
183-
.animate-bgGradient { background-size: 200% 200%; animation: bgGradient 20s ease infinite; }
184-
185-
@keyframes textGradient {
186-
0% { background-position: 0% 50%; }
187-
50% { background-position: 100% 50%; }
188-
100% { background-position: 0% 50%; }
189-
}
139+
/* Gradient Text Animation */
140+
@keyframes textGradient { 0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%} }
190141
.animate-textGradient { background-size: 200% 200%; animation: textGradient 6s ease-in-out infinite; }
191142
`}</style>
192143
</div>

src/components/Footer.tsx

Lines changed: 74 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,98 @@
11
"use client";
22

33
import { FaGithub, FaLinkedin } from "react-icons/fa";
4+
import { motion } from "framer-motion";
5+
import { useState, useEffect } from "react";
46

57
export default function Footer() {
8+
const [particles, setParticles] = useState<{ top: number; left: number; xOffset: number; delay: number }[]>([]);
9+
10+
useEffect(() => {
11+
setParticles(
12+
Array.from({ length: 20 }).map(() => ({
13+
top: Math.random() * 100,
14+
left: Math.random() * 100,
15+
xOffset: Math.random() * 300 - 150,
16+
delay: Math.random() * 10,
17+
}))
18+
);
19+
}, []);
20+
621
return (
7-
<footer className="relative mt-[100px] bg-gray-900 text-white pt-16 pb-8 overflow-hidden">
8-
{/* Animated Glowing Background */}
9-
<div className="absolute inset-0 -z-10 bg-gradient-to-r from-pink-600 via-purple-600 to-indigo-600 opacity-30 animate-gradientSlow blur-2xl"></div>
22+
<footer className="relative mt-[150px] text-white pt-20 pb-10 overflow-hidden">
23+
{/* Background Gradient */}
24+
<div className="absolute inset-0 -z-10 bg-gradient-to-r from-cyan-500 via-purple-600 to-pink-600 opacity-30 animate-gradientSlow blur-3xl"></div>
25+
26+
<div className="max-w-7xl mx-auto px-6 relative">
27+
<motion.div
28+
initial={{ y: 50, opacity: 0 }}
29+
whileInView={{ y: 0, opacity: 1 }}
30+
transition={{ duration: 1, ease: "easeOut" }}
31+
viewport={{ once: true }}
32+
className="rounded-3xl bg-white/10 backdrop-blur-2xl border border-white/20 shadow-2xl p-10 flex flex-col md:flex-row items-center justify-between gap-10 relative overflow-hidden"
33+
>
34+
<div className="absolute inset-0 rounded-3xl border-2 border-transparent bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 opacity-50 blur-md -z-10"></div>
35+
36+
<motion.h1 whileHover={{ scale: 1.05 }} className="text-3xl font-extrabold bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 bg-clip-text text-transparent tracking-wide">
37+
Hami Parsa
38+
</motion.h1>
1039

11-
<div className="max-w-7xl mx-auto px-6 flex flex-col md:flex-row items-center justify-between gap-8">
12-
{/* Copyright */}
13-
<p className="text-gray-300 text-center md:text-left hover:text-white transition-all duration-300 text-lg md:text-base font-semibold">
14-
&copy; {new Date().getFullYear()} Hami Parsa. All Rights Reserved.
15-
</p>
40+
<div className="flex gap-10">
41+
{[
42+
{ icon: <FaGithub size={32} />, link: "https://github.com/hamiparsa", color: "hover:shadow-pink-500/60" },
43+
{ icon: <FaLinkedin size={32} />, link: "https://www.linkedin.com/in/hami-parsa-146ba437a/", color: "hover:shadow-blue-500/60" },
44+
].map((item, i) => (
45+
<motion.a
46+
key={i}
47+
href={item.link}
48+
target="_blank"
49+
rel="noopener noreferrer"
50+
whileHover={{ scale: 1.3, rotate: 5 }}
51+
className={`text-gray-300 hover:text-white transition-all duration-300 transform hover:scale-125 hover:shadow-lg ${item.color}`}
52+
>
53+
{item.icon}
54+
</motion.a>
55+
))}
56+
</div>
1657

17-
{/* Social Links */}
18-
<div className="flex gap-8">
19-
<a
20-
href="https://github.com/hamiparsa"
21-
target="_blank"
22-
rel="noopener noreferrer"
23-
className="text-gray-300 hover:text-white transition-all duration-300 transform hover:scale-125 hover:shadow-lg hover:shadow-pink-500/60"
24-
>
25-
<FaGithub size={32} />
26-
</a>
27-
<a
28-
href="https://www.linkedin.com/in/hami-parsa-146ba437a/"
29-
target="_blank"
30-
rel="noopener noreferrer"
31-
className="text-gray-300 hover:text-blue-500 transition-all duration-300 transform hover:scale-125 hover:shadow-lg hover:shadow-blue-500/60"
32-
>
33-
<FaLinkedin size={32} />
34-
</a>
35-
</div>
58+
<motion.p initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.5 }} className="text-gray-300 font-medium text-center md:text-right text-lg">
59+
© {new Date().getFullYear()} Hami Parsa. All Rights Reserved.
60+
</motion.p>
61+
</motion.div>
3662
</div>
3763

38-
{/* Bottom Gradient Line with Glow */}
39-
<div className="mt-10 h-1 w-3/4 mx-auto rounded-full bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 animate-gradientFast shadow-2xl shadow-pink-500/50"></div>
64+
<motion.div initial={{ scaleX: 0 }} whileInView={{ scaleX: 1 }} transition={{ duration: 1.5, ease: "easeOut" }} viewport={{ once: true }} className="mt-14 h-1 w-2/3 mx-auto rounded-full bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 shadow-[0_0_20px_rgba(236,72,153,0.8)]"></motion.div>
4065

41-
{/* Extra Floating Glow Circles */}
42-
<div className="absolute -top-12 -left-12 w-32 h-32 bg-pink-500/30 rounded-full blur-3xl animate-floatSlow"></div>
43-
<div className="absolute -bottom-16 -right-16 w-40 h-40 bg-indigo-500/30 rounded-full blur-3xl animate-floatSlow delay-2000"></div>
66+
<div className="absolute -top-16 left-10 w-40 h-40 bg-cyan-500/30 rounded-full blur-3xl animate-floatSlow"></div>
67+
<div className="absolute bottom-0 right-10 w-52 h-52 bg-purple-500/30 rounded-full blur-3xl animate-floatSlow delay-2000"></div>
68+
69+
{/* Floating Neon Particles */}
70+
<div className="absolute inset-0 pointer-events-none">
71+
{particles.map((p, i) => (
72+
<motion.span
73+
key={i}
74+
className="absolute w-2 h-2 rounded-full bg-white/70"
75+
initial={{ opacity: 0, y: 0 }}
76+
animate={{ opacity: [0, 1, 0], y: [-20, -100], x: p.xOffset }}
77+
transition={{ duration: 5 + Math.random() * 5, repeat: Infinity, delay: p.delay }}
78+
style={{ top: `${p.top}%`, left: `${p.left}%` }}
79+
/>
80+
))}
81+
</div>
4482

4583
<style jsx>{`
4684
@keyframes gradientSlow {
4785
0% { background-position: 0% 50%; }
4886
50% { background-position: 100% 50%; }
4987
100% { background-position: 0% 50%; }
5088
}
51-
@keyframes gradientFast {
52-
0% { background-position: 0% 50%; }
53-
50% { background-position: 100% 50%; }
54-
100% { background-position: 0% 50%; }
55-
}
5689
@keyframes floatSlow {
5790
0%, 100% { transform: translateY(0px); }
58-
50% { transform: translateY(20px); }
59-
}
60-
.animate-gradientSlow {
61-
background-size: 200% 200%;
62-
animation: gradientSlow 25s ease infinite;
63-
}
64-
.animate-gradientFast {
65-
background-size: 200% 200%;
66-
animation: gradientFast 6s ease infinite;
67-
}
68-
.animate-floatSlow {
69-
animation: floatSlow 10s ease-in-out infinite;
70-
}
71-
.delay-2000 {
72-
animation-delay: 2s;
91+
50% { transform: translateY(30px); }
7392
}
93+
.animate-gradientSlow { background-size: 200% 200%; animation: gradientSlow 30s ease infinite; }
94+
.animate-floatSlow { animation: floatSlow 12s ease-in-out infinite; }
95+
.delay-2000 { animation-delay: 2s; }
7496
`}</style>
7597
</footer>
7698
);

0 commit comments

Comments
 (0)