refactor: update project layout and descriptions - Move project title to top - Simplify project descriptions - Adjust layout for better readability
Some checks failed
ci/cd / Build (push) Failing after 11s

This commit is contained in:
Mariia Shabelnik 2025-05-05 20:12:43 +02:00
parent 055975162a
commit 6a6c24ab20
10 changed files with 2188 additions and 1537 deletions

View File

@ -2,6 +2,15 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=5.0"
/>
<meta
name="description"
content="Mariia Shabelnik - Frontend Engineer portfolio showcasing web development projects and skills"
/>
<meta name="theme-color" content="#f0f0f3" />
<link <link
rel="icon" rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22>, <text y=%22.9em%22 font-size=%2290%22> href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22>, <text y=%22.9em%22 font-size=%2290%22>
@ -10,7 +19,6 @@
</svg>" </svg>"
/> />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- nav font --> <!-- nav font -->

3493
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@splinetool/react-spline": "^4.0.0",
"@splinetool/runtime": "^1.9.89",
"hamburger-react": "^2.5.0", "hamburger-react": "^2.5.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -21,10 +23,10 @@
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.15", "@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.0.0", "@vitejs/plugin-react": "^4.4.1",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.15",
"postcss": "^8.4.29", "postcss": "^8.4.29",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",
"vite": "^3.0.0" "vite": "^6.3.5"
} }
} }

View File

@ -1,11 +1,11 @@
//import "./App.css"; //import "./App.css";
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
import Footer from "./components/Footer";
import Header from "./components/Header"; import Header from "./components/Header";
import ExperianceDetail from "./pages/ExperienceDetail"; import ExperianceDetail from "./pages/ExperienceDetail";
import FullPage from "./pages/FullPage"; import FullPage from "./pages/FullPage";
import { RecoilRoot } from "recoil"; import { RecoilRoot } from "recoil";
import ScrollToAnchor from "./components/ScrollToAnchor"; import ScrollToAnchor from "./components/ScrollToAnchor";
/* import Spline from "@splinetool/react-spline"; */
function App() { function App() {
return ( return (
@ -14,13 +14,15 @@ function App() {
<main className=" min-h-screen"> <main className=" min-h-screen">
<Header /> <Header />
<ScrollToAnchor /> <ScrollToAnchor />
{/* <div className="w-full h-screen">
<Spline scene="https://prod.spline.design/magHjLGpPc2Y1jP4/scene.splinecode" />
</div> */}
<Routes> <Routes>
<Route path="/" element={<FullPage />} /> <Route path="/" element={<FullPage />} />
{/* <Route path="/experience" element={<Experience />} /> {/* <Route path="/experience" element={<Experience />} />
<Route path="/contact" element={<Contact />} />*/} <Route path="/contact" element={<Contact />} />*/}
<Route path="/experience/:id" element={<ExperianceDetail />} /> <Route path="/experience/:id" element={<ExperianceDetail />} />
</Routes> </Routes>
<Footer />
</main> </main>
</RecoilRoot> </RecoilRoot>
</div> </div>

View File

@ -1,10 +1,35 @@
import { FaLinkedin, FaEnvelope } from "react-icons/fa";
function Footer() { function Footer() {
const fullYear = new Date().getFullYear(); const fullYear = new Date().getFullYear();
const email = "mariia.shabelnik@gmail.com"; // Replace with your email
const linkedinUrl = "https://www.linkedin.com/in/mariia-shabelnik/"; // Replace with your LinkedIn URL
return ( return (
<footer className="container mx-auto my-10 px-6 sticky top-[95vh]"> <footer className="container mx-auto my-10 px-6 sticky top-[95vh]">
<div className=" text-center text-sm text-white/50 "> <div className="flex flex-col items-center gap-4">
<div className="flex gap-6">
<a
href={`mailto:${email}`}
className="text-gray-500 hover:text-highlight transition-colors duration-300"
aria-label="Send email to Mariia"
>
<FaEnvelope className="w-6 h-6" />
</a>
<a
href={linkedinUrl}
target="_blank"
rel="noopener noreferrer"
className="text-gray-500 hover:text-highlight transition-colors duration-300"
aria-label="Visit Mariia's LinkedIn profile"
>
<FaLinkedin className="w-6 h-6" />
</a>
</div>
<div className="text-center text-sm text-gray-500">
© {fullYear} Mariia Shabelnik, all rights reserved © {fullYear} Mariia Shabelnik, all rights reserved
</div> </div>
</div>
</footer> </footer>
); );
} }

View File

@ -20,7 +20,6 @@ function Header() {
const menuUI = menu.map((item) => { const menuUI = menu.map((item) => {
let className = "drop-shadow-doublelight hover:drop-shadow-active"; let className = "drop-shadow-doublelight hover:drop-shadow-active";
console.log(location);
if (`/${location.hash}` === item.link) { if (`/${location.hash}` === item.link) {
className += " text-white"; className += " text-white";
} else { } else {
@ -34,6 +33,9 @@ function Header() {
setOpen(false); setOpen(false);
}} }}
to={item.link} to={item.link}
aria-current={
location.hash === item.link.slice(2) ? "page" : undefined
}
> >
{item.title} {item.title}
</Link> </Link>
@ -74,27 +76,54 @@ function Header() {
return ( return (
<> <>
{isOpen && ( {isOpen && (
<div className={overlayMenu.join(" ")}> <nav
className={overlayMenu.join(" ")}
aria-label="Mobile navigation menu"
role="navigation"
>
<div className="h-[calc(100vh-20rem)] flex flex-col justify-center"> <div className="h-[calc(100vh-20rem)] flex flex-col justify-center">
<ul>{menuUI}</ul> <ul role="menu" aria-label="Mobile menu items">
</div> {menuUI}
</ul>
</div> </div>
</nav>
)} )}
<header className={headerClasses.join(" ")}> <header className={headerClasses.join(" ")}>
<div className="container mx-auto h-full max-w-7xl"> <div className="container mx-auto h-full max-w-7xl">
<nav className="flex items-center h-full px-10 "> <nav
className="flex items-center h-full px-10"
aria-label="Main navigation"
role="navigation"
>
<div className="flex-none text-3xl md:text-4xl font-logo text-[#6d6d6d]"> <div className="flex-none text-3xl md:text-4xl font-logo text-[#6d6d6d]">
<Link className={logoClasses.join(" ")} to="/#start"> <Link
className={logoClasses.join(" ")}
to="/#start"
aria-label="Home"
>
MS MS
</Link> </Link>
</div> </div>
<div className="grow"></div> <div className="grow"></div>
<div className="flex-none hidden md:block"> <div className="flex-none hidden md:block">
<ul className="flex flex-row gap-4 text-lg">{menuUI}</ul> <ul
className="flex flex-row gap-4 text-lg"
role="menu"
aria-label="Main menu items"
>
{menuUI}
</ul>
</div> </div>
<div className="flex-none block md:hidden"> <div className="flex-none block md:hidden">
<Hamburger toggled={isOpen} toggle={setOpen} duration={0.9} /> <Hamburger
toggled={isOpen}
toggle={setOpen}
duration={0.9}
aria-label="Toggle mobile menu"
aria-expanded={isOpen}
aria-controls="mobile-menu"
/>
</div> </div>
</nav> </nav>
</div> </div>

View File

@ -6,17 +6,20 @@ function About() {
const skills = useRecoilValue(skillsAtom); const skills = useRecoilValue(skillsAtom);
return ( return (
<section className="relative"> <section className="relative" aria-labelledby="about-heading">
<div id="about" className=" absolute "></div> <div id="about" className=" absolute "></div>
<div className=" min-h-screen flex items-center"> <div className=" min-h-screen flex items-center">
<div> <div>
<h2 className="mb-6 text-subTPhone md:text-subT font-headline"> <h2
id="about-heading"
className="mb-6 text-subTPhone md:text-subT font-headline"
>
About me<span className=" text-highlight">.</span> About me<span className=" text-highlight">.</span>
</h2> </h2>
<div className="flex flex-col md:flex-row gap-6"> <div className="flex flex-col md:flex-row gap-6">
<article className="basis-6/12 text-base font-body"> <article className="basis-6/12 text-base font-body">
<p> <p>
"Im a Stockholm-based developer passionate about building "I'm a Stockholm-based developer passionate about building
innovative, secure digital experiences with a focus on both innovative, secure digital experiences with a focus on both
frontend and backend technologies. With expertise in JavaScript, frontend and backend technologies. With expertise in JavaScript,
React, TypeScript, and a keen eye for web design, I create React, TypeScript, and a keen eye for web design, I create
@ -26,7 +29,7 @@ function About() {
have a deep passion for 3D design, which adds a unique, have a deep passion for 3D design, which adds a unique,
immersive dimension to my work. I thrive in collaborative immersive dimension to my work. I thrive in collaborative
environments that encourage continuous learning and value environments that encourage continuous learning and value
creative problem-solving. Lets collaborate to push the creative problem-solving. Let's collaborate to push the
boundaries of digital innovation!" boundaries of digital innovation!"
{/* As a prospective Frontend Developer, I seek a challenging career {/* As a prospective Frontend Developer, I seek a challenging career
opportunity in the IT industry, where I can collaborate with a opportunity in the IT industry, where I can collaborate with a
@ -39,21 +42,36 @@ function About() {
think you've got an opening that I might like, let's connect 🔗 */} think you've got an opening that I might like, let's connect 🔗 */}
</p> </p>
</article> </article>
<div className="border "> <figure className="border">
<img <img
className="rounded-3xl" className="rounded-3xl"
src="/public/img/ProfileMe.jpg" src="/public/img/ProfileMe.jpg"
alt="profile Image" alt="Mariia Shabelnik's profile photo"
/> />
</div> </figure>
<div className=" basis-1/3"> <aside className="basis-1/3" aria-labelledby="skills-heading">
<h4 className="mb-2 text-subTMini">Languages:</h4> <h2 id="skills-heading" className="sr-only">
Skills and Technologies
</h2>
<section aria-labelledby="languages-heading">
<h3 id="languages-heading" className="mb-2 text-subTMini">
Languages:
</h3>
<Tags listOfTags={skills.languages} /> <Tags listOfTags={skills.languages} />
<h4 className="my-2 text-subTMini">Frameworks:</h4>{" "} </section>
<section aria-labelledby="frameworks-heading">
<h3 id="frameworks-heading" className="my-2 text-subTMini">
Frameworks:
</h3>
<Tags listOfTags={skills.frameworks} /> <Tags listOfTags={skills.frameworks} />
<h4 className="my-2 text-subTMini">Tools I use:</h4>{" "} </section>
<section aria-labelledby="tools-heading">
<h3 id="tools-heading" className="my-2 text-subTMini">
Tools I use:
</h3>
<Tags listOfTags={skills.tools} /> <Tags listOfTags={skills.tools} />
</div> </section>
</aside>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,60 +10,60 @@ function Experiance() {
const even = key % 2; const even = key % 2;
return ( return (
<section <article
className={`flex-1 gap-4 mb-12 bg-[#f0f0f3] border shadow-box ${ className={`flex-1 gap-4 mb-12 bg-[#f0f0f3] border shadow-box ${
(even === 0 && "rounded-tl-3xl rounded-br-3xl") || (even === 0 && "rounded-tl-3xl rounded-br-3xl") ||
"rounded-tr-3xl rounded-bl-3xl" "rounded-tr-3xl rounded-bl-3xl"
}`} }`}
key={item.id} key={item.id}
> >
<div <div className={`flex pt-4 md:pt-14 flex-col `}>
className={`flex pt-4 md:pt-14 flex-col ${ <div className="px-4 mb-4">
even === 1 && "md:flex-row-reverse" <h3 className="text-subTMiniPhone md:text-subTMini font-ht">
}`} {item.title}
> </h3>
<div className=" basis-5/12">
<img className="w-full" src={item.previewImg} />
</div> </div>
<figure className="basis-5/12">
<img
className="w-full"
src={item.previewImg}
alt={`Preview of ${item.title} project`}
/>
</figure>
<div className="grow"></div> <div className="grow"></div>
<div className="flex flex-col px-4 pt-4 pb-8 basis-3/12"> <div className="flex flex-col px-4 pt-4 pb-8 basis-3/12">
<div> <div className="mb-4">
<h2 className="text-subTMiniPhone md:text-subTMini mb-4 font-ht"> <Tags listOfTags={item.tags} />
{item.title}
</h2>
</div> </div>
{/* <div className="text-base mb-6 line-clamp-3 md:line-clamp-6"> <div className="text-base mb-6">
<p>{item.info}</p> <p>{item.info}</p>
</div> */}
<Tags listOfTags={item.tags} />
<div className="grow"></div>
<div className=" text-left">
<Link
className=" drop-shadow-doublelight hover:drop-shadow-light "
to={`/experience/${item.id}`}
>
read more
</Link>
</div> </div>
</div> </div>
</div> </div>
</section> </article>
); );
}); });
return ( return (
<div className="relative"> <section className="relative" aria-labelledby="projects-heading">
<div id="experience" className=" absolute -top-16 "></div> <div
id="experience"
className="absolute -top-16"
aria-hidden="true"
></div>
<div className="min-h-screen"> <div className="min-h-screen">
<h2 className=" text-subTPhone md:text-subT font-headline mb-6"> <h2
id="projects-heading"
className="text-subTPhone md:text-subT font-headline mb-6"
>
Projects<span className="text-highlight">.</span> Projects<span className="text-highlight">.</span>
</h2> </h2>
<div className="py-4 flex flex-col md:flex-row gap-10"> <div className="py-4 flex flex-col md:flex-row gap-10" role="list">
{experianceListUI} {experianceListUI}
</div> </div>
</div> </div>
</div> </section>
); );
} }

View File

@ -1,15 +1,15 @@
import About from "./About"; import About from "./About";
import Contact from "./Contact"; import Footer from "../components/Footer";
import Experiance from "./Experience"; import Experiance from "./Experience";
import Start from "./Start"; import Start from "./Start";
function FullPage() { function FullPage() {
return ( return (
<div className="container mx-auto px-10 max-w-7xl"> <div className="container mx-auto px-10 max-w-6xl">
<Start /> <Start />
<About /> <About />
<Experiance /> <Experiance />
<Contact /> <Footer />
</div> </div>
); );
} }

View File

@ -4,7 +4,7 @@ const experianceList = [
{ {
id: "46bf76ae-8915-4e5d-ae92-4151be80e75a", id: "46bf76ae-8915-4e5d-ae92-4151be80e75a",
title: "Netzero web", title: "Netzero web",
info: "Discover the intersection of sustainability and technology in my collaboration with Net0. As the creative force behind the UI/UX design, I meticulously crafted a seamless and visually appealing user experience. Bringing this vision to life, I also spearheaded the frontend development, using React to construct an intuitive and efficient system for Net0. Immerse yourself in the synergy of eco-conscious practices and cutting-edge technology as we navigate towards a greener future, one React component at a time.", info: "Led UI/UX design and frontend development for Net0's sustainability platform. Built with React and TypeScript, featuring a modern interface that combines eco-conscious design with efficient user experience.",
img: ["/img/net0_0.png", "/img/net0_1.png", "/img/net0_2.png"], img: ["/img/net0_0.png", "/img/net0_1.png", "/img/net0_2.png"],
previewImg: "/img/mockup_net0.png", previewImg: "/img/mockup_net0.png",
tags: [ tags: [
@ -24,11 +24,11 @@ const experianceList = [
{ {
id: "55515a25-deb1-451c-bc7d-006d293f54aa", id: "55515a25-deb1-451c-bc7d-006d293f54aa",
title: "Lets fly", title: "Lets fly",
info: "Letsfly is a website aimed at providing a platform for aviation-related services and information. Built on Next.js, a popular React framework, the website leverages its server-side rendering (SSR) capability to enhance performance and user experience. Tailwind was used as the CSS framework to quickly and efficiently create an attractive and responsive design. WordPress, a well-known Content Management System (CMS), was employed as the content management system to administer and publish content on the website. The reason for choosing WordPress was a client request; he was familiar with this system and wanted to be able to modify the content himself. GraphQL was selected as the API layer to enable efficient data management and flexible data queries between the frontend and backend.", info: "Developed an aviation services platform using Next.js and WordPress. Implemented server-side rendering for optimal performance and created a responsive design with Tailwind CSS. Integrated GraphQL for efficient data management.",
img: ["/img/letsfly_1.png", "/img/letsfly_2.png", "/img/letsfly_3.png"], img: ["/img/letsfly_1.png", "/img/letsfly_2.png", "/img/letsfly_3.png"],
tags: ["React", "NextJS", "Wordpress", "GraphQL", "MySQL", "Tailwind"], tags: ["React", "NextJS", "Wordpress", "GraphQL", "MySQL", "Tailwind"],
link: "https://preview.letsfly.app/", link: "https://preview.letsfly.app/",
previewImg: "/img/mockup_net0.png", previewImg: "/img/letsfly_1.png",
}, },
]; ];