Added animation
All checks were successful
ci/cd / Build (push) Successful in 10s

This commit is contained in:
Mariia Shabelnik 2025-05-05 21:12:34 +02:00
parent 09cc4cb09b
commit fe38d6af18
4 changed files with 216 additions and 42 deletions

65
package-lock.json generated
View File

@ -8,11 +8,13 @@
"name": "portfolio",
"version": "0.0.0",
"dependencies": {
"framer-motion": "^12.9.7",
"hamburger-react": "^2.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
"react-image-gallery": "^1.3.0",
"react-intersection-observer": "^9.16.0",
"react-router-dom": "^6.3.0",
"react-scroll-motion": "^0.3.0",
"recoil": "^0.7.7"
@ -1757,6 +1759,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "12.9.7",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.9.7.tgz",
"integrity": "sha512-Eo5TYU6sEPPy82GDx32PJm++G+AkBCrzxtEQOWLnpQX896Q3LFrsYhMZ5YO5ct4wL7wyHU6hqlrpYXeexKAevg==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.9.6",
"motion-utils": "^12.9.4",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -2093,6 +2122,21 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/motion-dom": {
"version": "12.9.6",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.9.6.tgz",
"integrity": "sha512-IK9pm5zU8BIp3FCoUGF3T7AHVLVOlXxlwco/bIbcnpBtyYb2gDQhdOzUh2KSDJVjYl1MZ9vdq8tnFTTahX2lfg==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.9.4"
}
},
"node_modules/motion-utils": {
"version": "12.9.4",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.9.4.tgz",
"integrity": "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -2480,6 +2524,21 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/react-intersection-observer": {
"version": "9.16.0",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz",
"integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==",
"license": "MIT",
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@ -3002,6 +3061,12 @@
"dev": true,
"license": "Apache-2.0"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",

View File

@ -9,11 +9,13 @@
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^12.9.7",
"hamburger-react": "^2.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
"react-image-gallery": "^1.3.0",
"react-intersection-observer": "^9.16.0",
"react-router-dom": "^6.3.0",
"react-scroll-motion": "^0.3.0",
"recoil": "^0.7.7"

View File

@ -2,15 +2,46 @@ import { Link } from "react-router-dom";
import { useRecoilValue } from "recoil";
import { projectsAtom } from "../store";
import Tags from "../components/Tags";
import { motion } from "framer-motion";
import { useInView } from "react-intersection-observer";
function Experiance() {
const experianceList = useRecoilValue(projectsAtom);
const [ref, inView] = useInView({
triggerOnce: true,
threshold: 0.1,
});
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
ease: "easeOut",
},
},
};
const experianceListUI = experianceList.map((item, key) => {
const even = key % 2;
return (
<article
<motion.article
ref={ref}
variants={itemVariants}
className={`flex-1 gap-4 mb-12 bg-[#f0f0f3] border shadow-box ${
(even === 0 && "rounded-tl-3xl rounded-br-3xl") ||
"rounded-tr-3xl rounded-bl-3xl"
@ -41,7 +72,7 @@ function Experiance() {
</div>
</div>
</div>
</article>
</motion.article>
);
});
@ -59,9 +90,15 @@ function Experiance() {
>
Projects and experience<span className="text-highlight">.</span>
</h2>
<div className="py-4 grid grid-cols-1 md:grid-cols-2 gap-8" role="list">
<motion.div
ref={ref}
variants={containerVariants}
initial="hidden"
animate={inView ? "visible" : "hidden"}
className="py-4 grid grid-cols-1 md:grid-cols-2 gap-8"
>
{experianceListUI}
</div>
</motion.div>
</div>
</section>
);

View File

@ -1,4 +1,32 @@
import { motion, useAnimation } from "framer-motion";
import { useEffect } from "react";
function Start() {
const greeting = "Hello, my name is Maria.";
const greetingArray = greeting.split("");
const role = "I'm a Frontend Engineer";
const roleArray = role.split("");
const controls = useAnimation();
const contentControls = useAnimation();
useEffect(() => {
// Start the second animation after the first line completes
const timer1 = setTimeout(() => {
controls.start("visible");
}, greetingArray.length * 100 + 500); // Total time for first line + 500ms delay
// Start the content animation after the second line completes
const timer2 = setTimeout(() => {
contentControls.start("visible");
}, (greetingArray.length + roleArray.length) * 100 + 1000); // Total time for both lines + 1000ms delay
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
};
}, []);
return (
<div className="relative">
<div id="start" className="absolute -top-16 "></div>
@ -6,14 +34,54 @@ function Start() {
<div className=" min-h-screen flex items-center">
<div className="">
<h1 className="text-titlePhone md:text-title md:leading-tight mb-2 font-ht">
Hello, my name is Maria<span className=" text-highlight">.</span>
{greetingArray.map((letter, index) => (
<motion.span
key={index}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
duration: 0.1,
delay: index * 0.1,
}}
>
{letter}
</motion.span>
))}
<span className="text-highlight">.</span>
</h1>
<h2 className="text-subTPhone md:text-subT font-ht">
I'm a<span className=" text-highlight"> Frontend Engineer</span>
{roleArray.map((letter, index) => (
<motion.span
key={index}
initial={{ opacity: 0 }}
animate={controls}
variants={{
visible: { opacity: 1 }
}}
transition={{
duration: 0.1,
delay: index * 0.1,
}}
>
{letter}
</motion.span>
))}
<span className="text-highlight">.</span>
</h2>
<motion.div
initial={{ opacity: 0 }}
animate={contentControls}
variants={{
visible: { opacity: 1 }
}}
transition={{
duration: 0.8,
ease: "easeOut"
}}
>
<p className="text-base md:w-1/2 w-full text-left my-4 font-body">
Im a Stockholm-based web developer with startup experience,
building things with JavaScript, React, and Node.js. Im also into
I'm a Stockholm-based web developer with startup experience,
building things with JavaScript, React, and Node.js. I'm also into
3D design and art, and my mini poodle,{" "}
<span className="font-black">Oreo</span>{" "}
<img
@ -21,35 +89,37 @@ function Start() {
alt="oreo_logo"
className="inline h-"
/>{" "}
, is always in my creative space. Lets chat!
{/* Hi, Im Mariia! Im a developer who loves building functional,
user-friendly web experiences with a creative twist. Ive been
, is always in my creative space. Let's chat!
{/* Hi, I'm Mariia! I'm a developer who loves building functional,
user-friendly web experiences with a creative twist. I've been
working as a junior frontend engineer in startups, gaining hands-on
experience. I use JavaScript, React, Node.js, and SQL, and Im
always exploring 3D design to add a little extra flair. Lets
connect and maybe youll meet my mini poodle,{" "}
experience. I use JavaScript, React, Node.js, and SQL, and I'm
always exploring 3D design to add a little extra flair. Let's
connect and maybe you'll meet my mini poodle,{" "}
<span className="font-black">Oreo</span>{" "}
<img
src="/img/oreo-logo.png"
alt="oreo_logo"
className=" inline h-"
/>{" "}
, whos always hanging out with me while I work! */}
{/* "Hi, Im Mariia! Im a frontend developer with experience in startup projects, working with JavaScript, React, Node.js, and SQL. I love combining my technical skills with my passion for art and design, especially through 3D projects. Lets connect and see what we can create together—also, say hi to my mini poodle, Oreo, whos always by my side in my creative space!" */}
, who's always hanging out with me while I work! */}
{/* "Hi, I'm Mariia! I'm a frontend developer with experience in startup projects, working with JavaScript, React, Node.js, and SQL. I love combining my technical skills with my passion for art and design, especially through 3D projects. Let's connect and see what we can create together—also, say hi to my mini poodle, Oreo, who's always by my side in my creative space!" */}
</p>
<div>
<a
href="https://www.linkedin.com/in/mariia-shabelnik/"
target="_blank"
className="flex items-center gap-2 hover:text-highlight transition-colors duration-300"
>
<img
src="/img/icons8-linkedin.svg"
alt="oreo_logo"
className=" inline h-"
alt="LinkedIn icon"
className="inline h-6"
/>
LinkedIn
</a>
</div>
</motion.div>
</div>{" "}
</div>
</div>