diff --git a/package-lock.json b/package-lock.json index e126ee3..50ddad7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ae9b5c8..ba80098 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/pages/Experience.jsx b/src/pages/Experience.jsx index 091566b..8bceac1 100644 --- a/src/pages/Experience.jsx +++ b/src/pages/Experience.jsx @@ -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 ( -
-
+ ); }); @@ -59,9 +90,15 @@ function Experiance() { > Projects and experience. -
+ {experianceListUI} -
+ ); diff --git a/src/pages/Start.jsx b/src/pages/Start.jsx index 370d576..295d35c 100644 --- a/src/pages/Start.jsx +++ b/src/pages/Start.jsx @@ -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 (
@@ -6,50 +34,92 @@ function Start() {

- Hello, my name is Maria. + {greetingArray.map((letter, index) => ( + + {letter} + + ))} + .

- I'm a Frontend Engineer + {roleArray.map((letter, index) => ( + + {letter} + + ))} + .

-

- 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,{" "} - Oreo{" "} - oreo_logo{" "} - , 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 I’m - always exploring 3D design to add a little extra flair. Let’s - connect and maybe you’ll meet my mini poodle,{" "} - Oreo{" "} - oreo_logo{" "} - , 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!" */} -

-
- + +

+ 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,{" "} + Oreo{" "} oreo_logo{" "} + , 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 I'm + always exploring 3D design to add a little extra flair. Let's + connect and maybe you'll meet my mini poodle,{" "} + Oreo{" "} + oreo_logo - LinkedIn - -

+ />{" "} + , 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!" */} +

+
+ + LinkedIn icon + LinkedIn + +
+
{" "}