labguage and colormode support
This commit is contained in:
@@ -4,5 +4,5 @@ main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
/* padding: 2rem; */
|
||||
}
|
||||
|
||||
29
src/Context/LangContext.jsx
Normal file
29
src/Context/LangContext.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
const LangContext = createContext();
|
||||
|
||||
export const useLang = () => {
|
||||
return useContext(LangContext);
|
||||
};
|
||||
|
||||
const LangProvider = ({ children }) => {
|
||||
const [language, setLanguage] = useState(() => {
|
||||
return localStorage.getItem("language") || "en-GB";
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("language", language);
|
||||
}, [language]);
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage((prevLang) => (prevLang === "en-GB" ? "de-DE" : "en-GB"));
|
||||
};
|
||||
|
||||
return (
|
||||
<LangContext.Provider value={{ language, toggleLanguage }}>
|
||||
{children}
|
||||
</LangContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default LangProvider;
|
||||
32
src/Context/ThemeContext.jsx
Normal file
32
src/Context/ThemeContext.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
// Create the ThemeContext
|
||||
const ThemeContext = createContext();
|
||||
|
||||
// Custom hook for consuming ThemeContext
|
||||
export const useTheme = () => {
|
||||
return useContext(ThemeContext);
|
||||
};
|
||||
|
||||
const ThemeProvider = ({ children }) => {
|
||||
const [colormode, setColormode] = useState(() => {
|
||||
return localStorage.getItem("colormode") || "dark"; // Default to dark mode
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("colormode", colormode);
|
||||
document.documentElement.setAttribute("data-theme", colormode); // For global CSS styling
|
||||
}, [colormode]);
|
||||
|
||||
const toggleColorMode = () => {
|
||||
setColormode((prevMode) => (prevMode === "dark" ? "light" : "dark"));
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ colormode, toggleColorMode }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeProvider;
|
||||
@@ -1,21 +1,29 @@
|
||||
import React, { useState } from "react";
|
||||
import "../css/Hero.css";
|
||||
import "animate.css";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const Hero = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={`hero container animate__animated ${animation}`}
|
||||
className={`hero container animate__animated ${animation} ${colormode}`}
|
||||
onMouseEnter={() => onMouseEnter(setAnimation)}
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<p>
|
||||
<span>
|
||||
Hi, I'm Yousif. <br />
|
||||
{language === "en-GB" ? "Hi, I'm Yousif." : "Hi, ich bin Yousif."}{" "}
|
||||
<br />
|
||||
</span>{" "}
|
||||
I'm a Full-Stack Developer from <span>Germany</span>.
|
||||
{language === "en-GB"
|
||||
? "I'm a Full-Stack Developer from"
|
||||
: "Ich bin ein Full-Stack Entwickler aus"}{" "}
|
||||
<br />
|
||||
<span>{language === "en-GB" ? "Germany" : "Köln"}</span>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,18 +2,23 @@ import React from "react";
|
||||
import "../css/LangSelect.css";
|
||||
import { useState } from "react";
|
||||
import Toggle from "./subComponents/Toggle";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const LangSelect = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
const { language, toggleLanguage } = useLang();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`langSelect container animate__animated ${animation}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
className={`langSelect container animate__animated ${animation} ${colormode}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<div className="eng">🇬🇧</div>
|
||||
<Toggle />
|
||||
<div className="ger">🇩🇪</div>
|
||||
<Toggle toggled={language === "de-DE"} onToggle={toggleLanguage} /> <div className="ger">🇩🇪</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import React from "react";
|
||||
import "../css/Location.css";
|
||||
import LocationPin from "../assets/icons/navIcons/location-pin.svg?react";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const Location = () => {
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
|
||||
return (
|
||||
<div className="location container">
|
||||
<div className={`location container ${colormode}`}>
|
||||
<LocationPin className="navIcon" />
|
||||
<p>
|
||||
<strong>Cologne</strong>, Germany
|
||||
<strong>{`${language === "en-GB" ? "Cologne" : "Köln"}`}</strong>,{" "}
|
||||
{`${language === "en-GB" ? "Germany" : "Deutschland"}`}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,15 +2,19 @@ import React from "react";
|
||||
import "../css/Mail.css";
|
||||
import { useState } from "react";
|
||||
import EnvelopeIcon from "../assets/icons/navIcons/envelope.svg?react";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
|
||||
const Mail = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
const emailUser = "yousif.paulus";
|
||||
const domain = "hotmail.de";
|
||||
|
||||
const { colormode } = useTheme();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`eMail container animate__animated ${animation}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
className={`eMail container animate__animated ${animation} ${colormode}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<a
|
||||
|
||||
@@ -9,21 +9,52 @@ import SuitcaseIcon from "../assets/icons/navIcons/suitcase.svg?react";
|
||||
import DownloadIcon from "../assets/icons/navIcons/download.svg?react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import clsx from "clsx";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const Navigation = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
|
||||
const navItems = [
|
||||
{ name: "Home", to: "/", icon: HouseIcon, id: 1 },
|
||||
{ name: "About", to: "/about", icon: AddressCardIcon, id: 2 },
|
||||
{ name: "Experience", to: "/workExp", icon: SuitcaseIcon, id: 3 },
|
||||
{ name: "Projects", to: "/projects", icon: CodeIcon, id: 4 },
|
||||
{ name: "Contact", to: "/contact", icon: EnvelopeIcon, id: 5 },
|
||||
{ name: "CV", to: "", icon: DownloadIcon, id: 6 },
|
||||
{ name: { enGB: "Home", deDE: "Start" }, to: "/", icon: HouseIcon, id: 1 },
|
||||
{
|
||||
name: { enGB: "About", deDE: "Über Mich" },
|
||||
to: "/about",
|
||||
icon: AddressCardIcon,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
name: { enGB: "Experience", deDE: "Erfahrung" },
|
||||
to: "/workExp",
|
||||
icon: SuitcaseIcon,
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
name: { enGB: "Projects", deDE: "Projekte" },
|
||||
to: "/projects",
|
||||
icon: CodeIcon,
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
name: { enGB: "Contact", deDE: "Kontakt" },
|
||||
to: "/contact",
|
||||
icon: EnvelopeIcon,
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
name: { enGB: "CV", deDE: "Lebenslauf" },
|
||||
to: "",
|
||||
icon: DownloadIcon,
|
||||
id: 6,
|
||||
},
|
||||
];
|
||||
|
||||
const { language } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={`navigation container animate__animated ${animation}`}
|
||||
className={`navigation container animate__animated ${animation} ${colormode}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
@@ -38,11 +69,14 @@ const Navigation = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
clsx(
|
||||
"navLink",
|
||||
{ active: isActive },
|
||||
item.name === "CV" && "cv" // Add "cv" class if item.name is "CV"
|
||||
item.name.enGB === "CV" && "cv" // Add "cv" class if item.name is "CV"
|
||||
)
|
||||
}
|
||||
>
|
||||
<span>{item.name}</span> <IconComponent className="navIcon" />
|
||||
<span>
|
||||
{language === "en-GB" ? item.name.enGB : item.name.deDE}
|
||||
</span>{" "}
|
||||
<IconComponent className="navIcon" />
|
||||
</NavLink>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import "animate.css";
|
||||
import styled from "styled-components";
|
||||
import SunIcon from "../assets/icons/colorModeToggle/sun.svg?react";
|
||||
import MoonIcon from "../assets/icons/colorModeToggle/moon.svg?react";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
|
||||
const SwitchBtn = styled.button`
|
||||
background-color: ${(props) =>
|
||||
@@ -20,8 +21,8 @@ const SwitchBtn = styled.button`
|
||||
`;
|
||||
|
||||
const SwitchContainer = styled.div`
|
||||
transition: background-color 1s ease;
|
||||
grid-area: 8 / 4 / 10 / 5;
|
||||
transition: background-color 1s ease;
|
||||
background-color: ${(props) =>
|
||||
props.colormode === "dark" ? "#131862" : "#69d0ff"};
|
||||
display: flex;
|
||||
@@ -46,28 +47,19 @@ const PlanetsContainer = styled.div`
|
||||
`;
|
||||
|
||||
const Switch = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
const [colormode, setcolormode] = useState(() => {
|
||||
// Retrieve initial color mode from localStorage or default to "dark"
|
||||
return localStorage.getItem("colormode") || "dark";
|
||||
});
|
||||
const { colormode, toggleColorMode } = useTheme();
|
||||
const [rotation, setRotation] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Save the current color mode to localStorage whenever it changes
|
||||
localStorage.setItem("colormode", colormode);
|
||||
}, [colormode]);
|
||||
|
||||
const toggleColorMode = () => {
|
||||
setcolormode((prevMode) => (prevMode === "dark" ? "light" : "dark"));
|
||||
const handleClick = () => {
|
||||
toggleColorMode();
|
||||
setRotation((prevRotation) => prevRotation + 90);
|
||||
};
|
||||
|
||||
return (
|
||||
<SwitchContainer
|
||||
colormode={colormode}
|
||||
className={`container animate__animated ${animation}`}
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
className={`container animate__animated ${initialAnimation}`}
|
||||
onAnimationEnd={() => onAnimationEnd && onAnimationEnd()}
|
||||
>
|
||||
<div className="wrapper">
|
||||
<PlanetsContainer rotation={rotation} className="planets">
|
||||
@@ -100,7 +92,7 @@ const Switch = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
<SwitchBtn
|
||||
colormode={colormode}
|
||||
className={`colormodeBtn ${colormode}Btn`}
|
||||
onClick={toggleColorMode}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className={`colorToggleThumb ${colormode}`}></div>
|
||||
</SwitchBtn>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import React, { useState } from "react";
|
||||
import "../css/TallContent.css";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const TallContent = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`container tallContent animate__animated ${animation}`}
|
||||
className={`container tallContent animate__animated ${animation} ${colormode}`}
|
||||
onMouseEnter={() => onMouseEnter(setAnimation)}
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
|
||||
@@ -18,6 +18,8 @@ import Linux from "../assets/icons/skill-icons/icons/Linux-Dark.svg?react";
|
||||
import "../css/TechStack.css";
|
||||
import { Link } from "react-router-dom";
|
||||
import "animate.css";
|
||||
import { useTheme } from "../Context/ThemeContext";
|
||||
import { useLang } from "../Context/LangContext";
|
||||
|
||||
const TechStack = ({
|
||||
initialAnimation,
|
||||
@@ -95,17 +97,19 @@ const TechStack = ({
|
||||
];
|
||||
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"techStackContainer",
|
||||
"container",
|
||||
colormode === "light" ? "light" : "dark",
|
||||
"animate__animated",
|
||||
animation,
|
||||
{ expanded: isExpanded } // Conditional class
|
||||
)}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<h2>Tech Stack</h2>
|
||||
@@ -125,11 +129,13 @@ const TechStack = ({
|
||||
to="#"
|
||||
className="techStackShowMore"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent default link behavior
|
||||
onExpandToggle(); // Toggle the expanded state
|
||||
e.preventDefault();
|
||||
onExpandToggle();
|
||||
}}
|
||||
>
|
||||
{isExpanded ? "Show Less" : "Show More"} {/* Toggle link text */}
|
||||
{isExpanded
|
||||
? `${language === "en-GB" ? "Show Less" : "Weniger"}`
|
||||
: `${language === "en-GB" ? "Show More" : "Mehr"}`}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -18,14 +18,17 @@ import animedbImg1 from "../../assets/img/projects/animedb1.png";
|
||||
import animedbImg2 from "../../assets/img/projects/animedb2.png";
|
||||
import animedbImg3 from "../../assets/img/projects/animedb3.png";
|
||||
import strogasoImg from "../../assets/img/projects/strogaso.png";
|
||||
import { useLang } from "../../Context/LangContext";
|
||||
|
||||
const Projects = () => {
|
||||
const {language} = useLang()
|
||||
|
||||
const projectArr = [
|
||||
{
|
||||
name: "inkspire.",
|
||||
urls: { ghUrl: "https://github.com/yousifpa98/inkspire" },
|
||||
year: "2024",
|
||||
job: "freelance",
|
||||
job: language === "en-GB" ? "freelance" : "freiberuflich",
|
||||
imgs: [inkspireImg],
|
||||
id: 1,
|
||||
},
|
||||
@@ -36,7 +39,7 @@ const Projects = () => {
|
||||
liveUrl: "https://druckerei-eberwein.de/",
|
||||
},
|
||||
year: "2024",
|
||||
job: "freelance",
|
||||
job: language === "en-GB" ? "freelance" : "freiberuflich",
|
||||
imgs: [debImg],
|
||||
id: 2,
|
||||
},
|
||||
@@ -47,7 +50,7 @@ const Projects = () => {
|
||||
liveUrl: "https://otakucritics.netlify.app/",
|
||||
},
|
||||
year: "2024",
|
||||
job: "personal project",
|
||||
job: language === "en-GB" ? "personal project" : "pers. Projekt",
|
||||
imgs: [animeDBImg],
|
||||
id: 3,
|
||||
},
|
||||
@@ -76,6 +79,7 @@ const Projects = () => {
|
||||
const handleAnimationEnd = (setAnimation) => {
|
||||
setAnimation(""); // Clear animation to prevent re-triggering
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="gridContainer">
|
||||
<Navigation />
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import React from "react";
|
||||
import "../../../css/Specialties.css";
|
||||
import { useState } from "react";
|
||||
import { useTheme } from "../../../Context/ThemeContext";
|
||||
import { useLang } from "../../../Context/LangContext";
|
||||
|
||||
const Specialties = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={`specialties container animate__animated ${animation}`}
|
||||
className={`specialties container animate__animated ${animation} ${colormode}`}
|
||||
onMouseEnter={() => onMouseEnter(setAnimation)}
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<p className="specialtyText">
|
||||
I’m a fullstack developer who loves creating web applications that are
|
||||
functional and easy to use. My go-to tools are React, Node.js, and
|
||||
TypeScript, and I enjoy working on both the front-end and back-end. I
|
||||
focus on clean, practical solutions that work in the real world and
|
||||
approach every project with curiosity and attention to detail.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "I'm a fullstack developer who loves creating web applications that are functional and easy to use. My go-to tools are React, Node.js, and TypeScript, and I enjoy working on both the front-end and back-end. I focus on clean, practical solutions that work in the real world and approach every project with curiosity and attention to detail."
|
||||
: "Ich liebe es, funktionale und einfach zu bedienende Webanwendungen zu erstellen. Meine bevorzugten Tools sind React, Node.js und TypeScript, und ich arbeite gerne sowohl am Frontend als auch am Backend. Ich konzentriere mich auf saubere, praktische Lösungen, die in der realen Welt funktionieren, und gehe jedes Projekt mit Neugier und Liebe zum Detail an."
|
||||
}`}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import "../../../css/TallContentAbout.css";
|
||||
import { useTheme } from "../../../Context/ThemeContext";
|
||||
import { useLang } from "../../../Context/LangContext";
|
||||
|
||||
const TallContentAbout = ({
|
||||
initialAnimation,
|
||||
@@ -9,51 +11,86 @@ const TallContentAbout = ({
|
||||
}) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`container tallContentAbout animate__animated ${animation}`}
|
||||
className={`container tallContentAbout animate__animated ${animation} ${colormode}`}
|
||||
onMouseEnter={() => onMouseEnter(setAnimation)}
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<div className="funFacts">
|
||||
<h2>Some Fun-Facts about me:</h2>
|
||||
<h2>{`${
|
||||
language === "en-GB"
|
||||
? "Some Fun-Facts about me:"
|
||||
: "Ein paar Fun-Facts über mich:"
|
||||
}`}</h2>
|
||||
<ul className="funFactList">
|
||||
<li>
|
||||
<p>
|
||||
2300+ Chess.com blitz rating and a lifelong love for strategy.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "2300+ Chess.com blitz rating and a lifelong love for strategy."
|
||||
: "2300+ Blitz-Rating auf Chess.com und eine lange Liebe für Strategie"
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Big fan of tycoon simulation games—I could spend hours building
|
||||
and managing virtual worlds.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "Big fan of tycoon simulation games—I could spend hours building and managing virtual worlds."
|
||||
: "Großer Fan von Tycoon-Sim Spielen—Ich könnte Stunden damit verbringen, virtuelle Welten zu managen."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
I love Asian food, especially Japanese cuisine, and enjoy trying
|
||||
new recipes.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "I love Asian food, especially Japanese cuisine, and enjoy trying new recipes."
|
||||
: "Ich liebe asiatische Küche, besonders die japanische, und probiere gern neue Rezepte aus."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Football is my passion (and no, I won’t call it soccer).</p>
|
||||
<p>
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "Football is my passion (and no, I won't call it soccer)."
|
||||
: "Fußball ist eine meiner liebsten Leidenschaften."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
I have a guilty pleasure for sitcoms like The Big Bang Theory and
|
||||
can’t resist a good Taylor Swift track.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "I have a guilty pleasure for sitcoms like The Big Bang Theory and can't resist a good Taylor Swift track."
|
||||
: "Ich habe eine Schwäche für Sitcoms wie 'The Big Bang Theory' und kann einem guten Taylor Swift Song nicht wiederstehen."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
{/* <li>
|
||||
<p>Hiking is one of my favorite ways to relax and recharge.</p>
|
||||
</li> */}
|
||||
<li>
|
||||
<p>I enjoy baking, especially experimenting with new desserts.</p>
|
||||
<p>
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "I enjoy baking, especially experimenting with new desserts.."
|
||||
: "Ich backe gern und experimentiere gerne mit neuen Desserts."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
I’ve been drawing and painting since I was young, and it’s still
|
||||
one of my favorite creative outlets.
|
||||
{`${
|
||||
language === "en-GB"
|
||||
? "I've been drawing and painting since I was young, and it's still one of my favorite creative outlets."
|
||||
: "Ich zeichne und male seit meiner Kindheit - es ist bis heute eines meiner liebsten kreativen Hobbies."
|
||||
}`}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -2,10 +2,13 @@ import React from "react";
|
||||
import "../../../css/ProjectCard.css";
|
||||
import GitHubIcon from "../../../assets/icons/socials/square-github.svg?react";
|
||||
import GlobeIcon from "../../../assets/icons/socials/globe.svg?react";
|
||||
import { useTheme } from "../../../Context/ThemeContext";
|
||||
|
||||
const ProjectCard = ({ project }) => {
|
||||
const { colormode } = useTheme();
|
||||
|
||||
return (
|
||||
<div className="projectCard">
|
||||
<div className={`projectCard ${colormode}`}>
|
||||
<div className={project.imgs.length > 1 ? "imgs" : "img"}>
|
||||
{project.imgs.map((img, index) => (
|
||||
<img
|
||||
@@ -27,7 +30,7 @@ const ProjectCard = ({ project }) => {
|
||||
</h3>
|
||||
<p className="projectJob">{project.job}</p>
|
||||
<div className="projectLinks">
|
||||
{Object.entries(project.urls).map(([key, value]) => {
|
||||
{Object.entries(project.urls).map(([key, value]) => {
|
||||
// Determine the correct icon to render based on the key
|
||||
const Icon = key === "ghUrl" ? GitHubIcon : GlobeIcon;
|
||||
|
||||
@@ -39,7 +42,7 @@ const ProjectCard = ({ project }) => {
|
||||
rel="noopener noreferrer"
|
||||
href={value}
|
||||
>
|
||||
<Icon className="socialIcon"/>
|
||||
<Icon className="socialIcon" />
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
import "../../../css/ProjectsMain.css";
|
||||
import { useState } from "react";
|
||||
import ProjectCard from "./ProjectCard";
|
||||
import { useTheme } from "../../../Context/ThemeContext";
|
||||
import { useLang } from "../../../Context/LangContext";
|
||||
|
||||
const ProjectsMain = ({
|
||||
initialAnimation,
|
||||
@@ -11,17 +13,24 @@ const ProjectsMain = ({
|
||||
}) => {
|
||||
const [animation, setAnimation] = useState(initialAnimation);
|
||||
|
||||
const { colormode } = useTheme();
|
||||
const { language } = useLang();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`projectsMain container animate__animated ${animation}`}
|
||||
className={`projectsMain container animate__animated ${animation} ${colormode}`}
|
||||
/* onMouseEnter={() => onMouseEnter(setAnimation)} */
|
||||
onAnimationEnd={() => onAnimationEnd(setAnimation)}
|
||||
>
|
||||
<h2>Some of my latest projects:</h2>
|
||||
<h2>{`${
|
||||
language === "en-GB"
|
||||
? "Some of my latest projects:"
|
||||
: "Ein paar meiner letzten Projekte:"
|
||||
}`}</h2>
|
||||
<div className="projectsDiv">
|
||||
{projects.map((project) => (
|
||||
<ProjectCard key={project.id} project={project} />
|
||||
))}
|
||||
{projects.map((project) => (
|
||||
<ProjectCard key={project.id} project={project} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import "../../css/Toggle.css";
|
||||
|
||||
const Toggle = () => {
|
||||
const [toggled, setToggled] = useState(false);
|
||||
const Toggle = ({ toggled, onToggle }) => {
|
||||
return (
|
||||
<button
|
||||
className={`toggle-btn ${toggled ? "toggled" : ""}`}
|
||||
onClick={() => setToggled(!toggled)}
|
||||
onClick={onToggle}
|
||||
>
|
||||
<div className="thumb"></div>
|
||||
</button>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
grid-template-rows: repeat(9, 1fr);
|
||||
gap: 10px;
|
||||
width: 50%;
|
||||
min-width: 960px;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
@@ -13,6 +14,13 @@
|
||||
border-radius: 0.5rem;
|
||||
border: 0.5px solid rgb(73, 73, 73);
|
||||
box-shadow: 0 0 10px -5px rgba(184, 184, 184, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.container.light {
|
||||
background-color: white;
|
||||
border: 0.5px solid black;
|
||||
box-shadow: 0 0 10px -5px rgba(78, 78, 78, 0.4);
|
||||
}
|
||||
|
||||
.container:hover {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.hero {
|
||||
grid-area: 1 / 1 / 4 / 5;
|
||||
animation-duration: 1.5s;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hero p span {
|
||||
@@ -10,5 +11,9 @@
|
||||
|
||||
.hero p {
|
||||
font-size: 2rem;
|
||||
color: rgb(151, 151, 151);
|
||||
color: rgb(90, 90, 90);
|
||||
}
|
||||
|
||||
.hero.light p span {
|
||||
color: rgb(40, 40, 40);
|
||||
}
|
||||
|
||||
@@ -6,3 +6,11 @@
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.location.light {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.location.light .navIcon {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
@@ -17,3 +17,12 @@
|
||||
color: rgb(151, 151, 151);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
.eMail.light .emailAnchor {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.eMail.light .copyright {
|
||||
color: rgb(102, 102, 102);
|
||||
}
|
||||
@@ -40,3 +40,15 @@
|
||||
font-weight: bold;
|
||||
transition: font-weight 0.3s ease, border-bottom 0.3s ease;
|
||||
}
|
||||
|
||||
.navigation.light .navLink {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navigation.light .navLink.active span {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.navigation.light .navIcon {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
margin-bottom: 0.245rem;
|
||||
}
|
||||
|
||||
.projectInfo h3 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.projectInfo h3 span {
|
||||
color: rgb(151, 151, 151);
|
||||
font-size: 1rem;
|
||||
@@ -70,3 +66,15 @@
|
||||
justify-content: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.projectCard.light .projectInfo h3 span, .projectCard.light .projectJob{
|
||||
color: rgb(90, 90, 90);
|
||||
}
|
||||
|
||||
.projectCard.light .socialIcon {
|
||||
fill: #333;
|
||||
}
|
||||
|
||||
.projectCard.light .socialIcon:hover {
|
||||
fill: #6e6e6e;
|
||||
}
|
||||
@@ -7,3 +7,7 @@
|
||||
font-size: 1.2rem;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.specialties.light .specialtyText {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
.moon2 {
|
||||
bottom: 25px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transform: translateX(-50%) rotate(180deg);
|
||||
}
|
||||
|
||||
/* Left corner */
|
||||
|
||||
@@ -35,3 +35,7 @@
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.tallContentAbout.light .funFactList li p {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -1,66 +1,74 @@
|
||||
.techStackContainer {
|
||||
grid-area: 4 / 1 / 8 / 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
grid-area: 4 / 1 / 8 / 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
.techStackContainer.expanded {
|
||||
grid-area: 4 / 1 / 10 / 5;
|
||||
min-width: 356.43px;
|
||||
}
|
||||
.techStackContainer.expanded {
|
||||
grid-area: 4 / 1 / 10 / 5;
|
||||
min-width: 356.43px;
|
||||
}
|
||||
|
||||
.techStackContainer h2 {
|
||||
margin-bottom: 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.techStackContainer h2 {
|
||||
margin-bottom: 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.techIcon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
filter: grayscale(0.7);
|
||||
transition: transform 0.3s ease, filter 0.3s ease;
|
||||
}
|
||||
.techIcon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
filter: grayscale(0.7);
|
||||
transition: transform 0.3s ease, filter 0.3s ease;
|
||||
}
|
||||
|
||||
.techIcon:hover {
|
||||
filter: none;
|
||||
animation: tada 1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.techStackContainer.light .techIcon {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.techStack {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.techIcon:hover {
|
||||
filter: none;
|
||||
animation: tada 1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.techStackShowMore {
|
||||
font-size: 1.2rem;
|
||||
color: rgb(151, 151, 151);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgb(151, 151, 151);
|
||||
/* align-self: flex-start; */
|
||||
}
|
||||
.techStack {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@keyframes techIconHover {
|
||||
10% {
|
||||
transform: scale();
|
||||
}
|
||||
}
|
||||
.techStackShowMore {
|
||||
font-size: 1.2rem;
|
||||
color: rgb(151, 151, 151);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgb(151, 151, 151);
|
||||
/* align-self: flex-start; */
|
||||
}
|
||||
|
||||
/* Smooth expand/collapse animations */
|
||||
.techStackContainer {
|
||||
max-height: 320px; /* Initial max-height */
|
||||
overflow: hidden;
|
||||
transition: max-height 1s ease-in-out;
|
||||
}
|
||||
.techStackContainer.light .techStackShowMore {
|
||||
color: rgb(61, 61, 61);
|
||||
border-color: rgb(61, 61, 61);
|
||||
}
|
||||
|
||||
.techStackContainer.expanded {
|
||||
max-height: 600px; /* Expanded max-height */
|
||||
@keyframes techIconHover {
|
||||
10% {
|
||||
transform: scale();
|
||||
}
|
||||
}
|
||||
|
||||
/* Smooth expand/collapse animations */
|
||||
.techStackContainer {
|
||||
max-height: 320px; /* Initial max-height */
|
||||
overflow: hidden;
|
||||
transition: max-height 1s ease-in-out;
|
||||
}
|
||||
|
||||
.techStackContainer.expanded {
|
||||
max-height: 600px; /* Expanded max-height */
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* Reset and defaults */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -6,12 +7,40 @@
|
||||
|
||||
body {
|
||||
font-family: "SF Pro Display", sans-serif;
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
[data-theme="dark"] body {
|
||||
background-color: #000000;
|
||||
background-color: #000000;
|
||||
background-image: linear-gradient(43deg, #000000 29%, #454545 75%, #000 100%);
|
||||
}
|
||||
|
||||
[data-theme="light"] body {
|
||||
background-color: #ffffff;
|
||||
background-image: linear-gradient(
|
||||
43deg,
|
||||
#000000 29%,
|
||||
#454545 75%,
|
||||
#000 100%
|
||||
#ffffff 29%,
|
||||
#d7d7d7 75%,
|
||||
#ffffff 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
[data-theme="dark"] h1,
|
||||
[data-theme="dark"] h2,
|
||||
[data-theme="dark"] h3,
|
||||
[data-theme="dark"] h4,
|
||||
[data-theme="dark"] h5,
|
||||
[data-theme="dark"] h6 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
[data-theme="light"] h1,
|
||||
[data-theme="light"] h2,
|
||||
[data-theme="light"] h3,
|
||||
[data-theme="light"] h4,
|
||||
[data-theme="light"] h5,
|
||||
[data-theme="light"] h6 {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,15 @@ import "./index.css";
|
||||
import App from "./App.jsx";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import "./css/fonts.css";
|
||||
import ThemeProvider from "./Context/ThemeContext.jsx";
|
||||
import LangProvider from "./Context/LangContext.jsx";
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
<ThemeProvider>
|
||||
<LangProvider>
|
||||
<App />
|
||||
</LangProvider>
|
||||
</ThemeProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user