Pretty Ui Update
This commit is contained in:
@@ -26,9 +26,6 @@
|
|||||||
|
|
||||||
<meta name="robots" content="index, follow" />
|
<meta name="robots" content="index, follow" />
|
||||||
<link rel="canonical" href="https://space.reversed.dev" />
|
<link rel="canonical" href="https://space.reversed.dev" />
|
||||||
|
|
||||||
<script defer src="https://not-a-tracker.reversed.dev/script.js"
|
|
||||||
data-website-id="7d28af45-d984-428e-af79-fb4dc7e91492"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -10,9 +10,12 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@danielgtmn/umami-react": "^1.1.6",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
|
"lucide-react": "^0.577.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
"react-router-dom": "^7.13.1",
|
||||||
"tailwindcss": "^4.1.17"
|
"tailwindcss": "^4.1.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
72
pnpm-lock.yaml
generated
72
pnpm-lock.yaml
generated
@@ -8,15 +8,24 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@danielgtmn/umami-react':
|
||||||
|
specifier: ^1.1.6
|
||||||
|
version: 1.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.17
|
specifier: ^4.1.17
|
||||||
version: 4.1.17(rolldown-vite@7.2.5(@types/node@24.10.1)(jiti@2.6.1))
|
version: 4.1.17(rolldown-vite@7.2.5(@types/node@24.10.1)(jiti@2.6.1))
|
||||||
|
lucide-react:
|
||||||
|
specifier: ^0.577.0
|
||||||
|
version: 0.577.0(react@19.2.0)
|
||||||
react:
|
react:
|
||||||
specifier: ^19.2.0
|
specifier: ^19.2.0
|
||||||
version: 19.2.0
|
version: 19.2.0
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.2.0
|
specifier: ^19.2.0
|
||||||
version: 19.2.0(react@19.2.0)
|
version: 19.2.0(react@19.2.0)
|
||||||
|
react-router-dom:
|
||||||
|
specifier: ^7.13.1
|
||||||
|
version: 7.13.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.1.17
|
specifier: ^4.1.17
|
||||||
version: 4.1.17
|
version: 4.1.17
|
||||||
@@ -143,6 +152,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@danielgtmn/umami-react@1.1.6':
|
||||||
|
resolution: {integrity: sha512-vNk1Q8JOjbE1wx1vM5u2E6jPIDSrZJM/mg++ButiVtD56LNENb1cHvWKcQqC9/camx90PrmdKkWaYhVwU8fCEg==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.2.0 || ^19.0.0
|
||||||
|
react-dom: ^18.2.0 || ^19.0.0
|
||||||
|
|
||||||
'@emnapi/core@1.7.1':
|
'@emnapi/core@1.7.1':
|
||||||
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
|
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
|
||||||
|
|
||||||
@@ -586,6 +602,10 @@ packages:
|
|||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
cookie@1.1.1:
|
||||||
|
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -910,6 +930,11 @@ packages:
|
|||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
|
lucide-react@0.577.0:
|
||||||
|
resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.21:
|
||||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||||
|
|
||||||
@@ -1001,6 +1026,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
|
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
react-router-dom@7.13.1:
|
||||||
|
resolution: {integrity: sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=18'
|
||||||
|
react-dom: '>=18'
|
||||||
|
|
||||||
|
react-router@7.13.1:
|
||||||
|
resolution: {integrity: sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==}
|
||||||
|
engines: {node: '>=20.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=18'
|
||||||
|
react-dom: '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
react@19.2.0:
|
react@19.2.0:
|
||||||
resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
|
resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1073,6 +1115,9 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
set-cookie-parser@2.7.2:
|
||||||
|
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -1284,6 +1329,11 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.28.5
|
'@babel/helper-validator-identifier': 7.28.5
|
||||||
|
|
||||||
|
'@danielgtmn/umami-react@1.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.2.0
|
||||||
|
react-dom: 19.2.0(react@19.2.0)
|
||||||
|
|
||||||
'@emnapi/core@1.7.1':
|
'@emnapi/core@1.7.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/wasi-threads': 1.1.0
|
'@emnapi/wasi-threads': 1.1.0
|
||||||
@@ -1725,6 +1775,8 @@ snapshots:
|
|||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
|
cookie@1.1.1: {}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@@ -2013,6 +2065,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
|
|
||||||
|
lucide-react@0.577.0(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.2.0
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.21:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
@@ -2090,6 +2146,20 @@ snapshots:
|
|||||||
|
|
||||||
react-refresh@0.18.0: {}
|
react-refresh@0.18.0: {}
|
||||||
|
|
||||||
|
react-router-dom@7.13.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.2.0
|
||||||
|
react-dom: 19.2.0(react@19.2.0)
|
||||||
|
react-router: 7.13.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
|
|
||||||
|
react-router@7.13.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
cookie: 1.1.1
|
||||||
|
react: 19.2.0
|
||||||
|
set-cookie-parser: 2.7.2
|
||||||
|
optionalDependencies:
|
||||||
|
react-dom: 19.2.0(react@19.2.0)
|
||||||
|
|
||||||
react@19.2.0: {}
|
react@19.2.0: {}
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
@@ -2140,6 +2210,8 @@ snapshots:
|
|||||||
|
|
||||||
semver@7.7.3: {}
|
semver@7.7.3: {}
|
||||||
|
|
||||||
|
set-cookie-parser@2.7.2: {}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
|
|||||||
272
src/App.tsx
272
src/App.tsx
@@ -1,23 +1,22 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||||
|
import UmamiAnalytics from "@danielgtmn/umami-react";
|
||||||
import type { Experience, MiniProject, Project } from "./types";
|
import type { Experience, MiniProject, Project } from "./types";
|
||||||
import { MiniProjectModal } from "./components/MiniProjectModal";
|
import { MiniProjectModal } from "./components/MiniProjectModal";
|
||||||
import { ExperienceModal } from "./components/ExperienceModal";
|
import { ExperienceModal } from "./components/ExperienceModal";
|
||||||
import { ProjectCard } from "./components/ProjectCard";
|
import { Navbar } from "./components/Navbar";
|
||||||
import { MiniProjectCard } from "./components/MiniProjectCard";
|
|
||||||
import { ExperienceCard } from "./components/ExperienceCard";
|
|
||||||
import { getTypeIcon } from "./components/utils";
|
|
||||||
import { Hero } from "./sections/Hero";
|
|
||||||
import { About } from "./sections/About";
|
|
||||||
import { Goals } from "./sections/Goals";
|
|
||||||
import { InternshipSection } from "./sections/InternshipSection";
|
|
||||||
import { Contact } from "./sections/Contact";
|
|
||||||
import { Footer } from "./sections/Footer";
|
import { Footer } from "./sections/Footer";
|
||||||
|
|
||||||
|
// Pages
|
||||||
|
import { Home } from "./pages/Home";
|
||||||
|
import { About } from "./pages/AboutPage";
|
||||||
|
import { Contact } from "./pages/ContactPage";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [status, setStatus] = useState("");
|
const [status, setStatus] = useState("");
|
||||||
const [borderStatus, setBorderStatus] = useState("border-gray-700");
|
const [borderStatus, setBorderStatus] = useState("border-gray-700");
|
||||||
const [glowColor, setGlowColor] = useState("rgba(55, 65, 81, 0.5)");
|
const [glowColor, setGlowColor] = useState("rgba(55, 65, 81, 0.5)");
|
||||||
const [statusMessage, setStatusMessage] = useState("[🔃🔃🔃]");
|
const [statusMessage, setStatusMessage] = useState("[??????]");
|
||||||
const [messageIndex, setMessageIndex] = useState(0);
|
const [messageIndex, setMessageIndex] = useState(0);
|
||||||
const [displayMessage, setDisplayMessage] = useState("");
|
const [displayMessage, setDisplayMessage] = useState("");
|
||||||
const [isScrambling, setIsScrambling] = useState(false);
|
const [isScrambling, setIsScrambling] = useState(false);
|
||||||
@@ -33,30 +32,28 @@ function App() {
|
|||||||
|
|
||||||
const oldUsernames = [
|
const oldUsernames = [
|
||||||
"getspaced (ingame)",
|
"getspaced (ingame)",
|
||||||
"Space² (alternative)",
|
"Space<EFBFBD> (alternative)",
|
||||||
"Space-Banane (2022-2024)",
|
"Space-Banane (2022-2024)",
|
||||||
];
|
];
|
||||||
|
|
||||||
const rotatingMessages = [
|
const rotatingMessages = [
|
||||||
"Yelling at the compiler ⁉️",
|
"Yelling at Claude",
|
||||||
"Shipping bug fixes 📦",
|
"Shipping bugs",
|
||||||
"Waiting for CI ✅",
|
"Compiling typescript files... Please wait.",
|
||||||
"No debugger attached 🐞",
|
"Waiting for CI, this might take a while...",
|
||||||
"Yelling at Gemini 🤖",
|
"No debugger attached, but I'm sure it works ?",
|
||||||
"Writing Readme files 📝",
|
"Seems fine, must be a problem with your machine lol",
|
||||||
"Collecting Spotify hours 🎵",
|
"Waiting for pip, this might take a lifetime...",
|
||||||
"Downloading 10 bajillion dependencies 📥",
|
"Collecting Spotify hours, this might take a while...",
|
||||||
"Fighting with package managers 📦",
|
"Fighting with go modules, i lost",
|
||||||
"Considering nuking python from orbit 🐍",
|
"Nuking Python for the 50th time, it's winning",
|
||||||
"Why is uv so cool? 🧊",
|
"Why is uv so cool?",
|
||||||
"I'm hungry 🍔",
|
"Pulling 20-ish GB of node modules, see you next year"
|
||||||
"I swear this worked on my machine 🖥️",
|
|
||||||
"Pulling 10GB of docker images 🐳",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const characters =
|
const characters =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:',.<>?/`~" +
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:',.<>?/`~" +
|
||||||
"🧱⁉️🧪📦🦀✅🚧🐞🤖🔄🥹🧹🎵";
|
"?????????????????????????";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === "online") {
|
if (status === "online") {
|
||||||
@@ -78,7 +75,7 @@ function App() {
|
|||||||
} else {
|
} else {
|
||||||
setBorderStatus("border-gray-700");
|
setBorderStatus("border-gray-700");
|
||||||
setGlowColor("rgba(55, 65, 81, 0.5)");
|
setGlowColor("rgba(55, 65, 81, 0.5)");
|
||||||
setStatusMessage("[🔃🔃🔃]");
|
setStatusMessage("[??????]");
|
||||||
}
|
}
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
@@ -123,8 +120,8 @@ function App() {
|
|||||||
setIsScrambling(false);
|
setIsScrambling(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
iteration += 1 / 3;
|
iteration += 1; // Faster: increment by 1 instead of 1/3
|
||||||
}, 25);
|
}, 15); // Faster: reduce interval to 15ms
|
||||||
|
|
||||||
return () => clearInterval(scrambleInterval);
|
return () => clearInterval(scrambleInterval);
|
||||||
} else {
|
} else {
|
||||||
@@ -169,77 +166,61 @@ function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-screen bg-black text-white selection:bg-purple-500/30">
|
<Router>
|
||||||
{/* Background Grid */}
|
<UmamiAnalytics
|
||||||
<div className="fixed inset-0 z-0 pointer-events-none">
|
websiteId="7d28af45-d984-428e-af79-fb4dc7e91492"
|
||||||
<div
|
url="https://not-a-tracker.reversed.dev/script.js"
|
||||||
className="absolute inset-0 blur-sm"
|
/>
|
||||||
style={{
|
<div className="relative min-h-screen bg-[#020205] text-white selection:bg-blue-500/30 font-sans overflow-x-hidden">
|
||||||
backgroundImage:
|
{/* Modern Background */}
|
||||||
"linear-gradient(#ffffff 1px, transparent 1px), linear-gradient(90deg, #ffffff 1px, transparent 1px)",
|
<div className="fixed inset-0 z-0 pointer-events-none">
|
||||||
backgroundSize: "30px 30px",
|
{/* Main Gradient Glows */}
|
||||||
maskImage:
|
<div className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] rounded-full bg-blue-600/10 blur-[120px] animate-pulse"></div>
|
||||||
"radial-gradient(circle at center, black 40%, transparent 80%)",
|
<div className="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] rounded-full bg-purple-600/10 blur-[120px] animate-pulse" style={{ animationDelay: "1s" }}></div>
|
||||||
}}
|
|
||||||
/>
|
{/* Subtle Grid Pattern */}
|
||||||
</div>
|
<div
|
||||||
|
className="absolute inset-0 opacity-[0.03]"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `linear-gradient(#fff 1px, transparent 1px), linear-gradient(90deg, #fff 1px, transparent 1px)`,
|
||||||
|
backgroundSize: "40px 40px"
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
{/* Noise Overlay */}
|
||||||
|
<div className="absolute inset-0 opacity-[0.02] mix-blend-overlay pointer-events-none bg-[url('https://grainy-gradients.vercel.app/noise.svg')]"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<main className="relative z-10 flex flex-col items-center px-6 py-20 mx-auto max-w-5xl gap-24">
|
<Navbar />
|
||||||
<Hero
|
|
||||||
glowColor={glowColor}
|
|
||||||
borderStatus={borderStatus}
|
|
||||||
displayMessage={displayMessage}
|
|
||||||
rotatingMessages={rotatingMessages}
|
|
||||||
statusMessage={statusMessage}
|
|
||||||
showOldNames={showOldNames}
|
|
||||||
setShowOldNames={setShowOldNames}
|
|
||||||
oldUsernames={oldUsernames}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<About />
|
<main className="relative z-10 pt-32 pb-20 px-6 mx-auto max-w-6xl">
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
element={
|
||||||
|
<Home
|
||||||
|
glowColor={glowColor}
|
||||||
|
borderStatus={borderStatus}
|
||||||
|
displayMessage={displayMessage}
|
||||||
|
rotatingMessages={rotatingMessages}
|
||||||
|
statusMessage={statusMessage}
|
||||||
|
showOldNames={showOldNames}
|
||||||
|
setShowOldNames={setShowOldNames}
|
||||||
|
oldUsernames={oldUsernames}
|
||||||
|
projects={projects}
|
||||||
|
miniProjects={miniProjects}
|
||||||
|
setSelectedMiniProject={setSelectedMiniProject}
|
||||||
|
experiences={experiences}
|
||||||
|
setSelectedExperience={setSelectedExperience}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/about" element={<About />} />
|
||||||
|
<Route path="/contact" element={<Contact />} />
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
|
||||||
<Goals />
|
<Footer />
|
||||||
|
|
||||||
{/* Projects Section */}
|
|
||||||
<section className="w-full space-y-12">
|
|
||||||
<div className="text-center space-y-2">
|
|
||||||
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-400 via-pink-500 to-orange-400">
|
|
||||||
Favourite Projects
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
|
||||||
Personal favourites, projects that I'm proud of, or just things I
|
|
||||||
find fun to show off.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{projects.map((project, index) => (
|
|
||||||
<ProjectCard key={index} project={project} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Mini Projects Section */}
|
|
||||||
<section className="w-full space-y-12">
|
|
||||||
<div className="text-center space-y-2">
|
|
||||||
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500">
|
|
||||||
Mini Projects
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
|
||||||
Small experiments and fun projects I've built along the way
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{miniProjects.map((project, index) => (
|
|
||||||
<MiniProjectCard
|
|
||||||
key={index}
|
|
||||||
project={project}
|
|
||||||
onClick={() => setSelectedMiniProject(project)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{selectedMiniProject && (
|
{selectedMiniProject && (
|
||||||
<MiniProjectModal
|
<MiniProjectModal
|
||||||
@@ -254,101 +235,8 @@ function App() {
|
|||||||
onClose={() => setSelectedExperience(null)}
|
onClose={() => setSelectedExperience(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
{/* Experience Section */}
|
</Router>
|
||||||
<section className="w-full space-y-8">
|
|
||||||
<div className="text-center space-y-2">
|
|
||||||
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-orange-400 via-red-500 to-pink-500">
|
|
||||||
Experience
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
|
||||||
Technologies, platforms, and roles I've worked with
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-4xl mx-auto space-y-6">
|
|
||||||
{Object.entries(
|
|
||||||
experiences.reduce(
|
|
||||||
(acc, exp) => {
|
|
||||||
if (!acc[exp.type]) acc[exp.type] = [];
|
|
||||||
acc[exp.type].push(exp);
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, Experience[]>,
|
|
||||||
),
|
|
||||||
).map(([type, items]) => (
|
|
||||||
<div key={type} className="space-y-3">
|
|
||||||
<h3
|
|
||||||
className={`text-lg font-semibold flex items-center gap-2 ${
|
|
||||||
type === "language"
|
|
||||||
? "text-blue-400"
|
|
||||||
: type === "service"
|
|
||||||
? "text-purple-400"
|
|
||||||
: type === "platform"
|
|
||||||
? "text-green-400"
|
|
||||||
: type === "real life experience"
|
|
||||||
? "text-orange-400"
|
|
||||||
: type === "roles"
|
|
||||||
? "text-pink-400"
|
|
||||||
: "text-gray-400"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{getTypeIcon(type as Experience["type"])}{" "}
|
|
||||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
{items.map((exp, index) => (
|
|
||||||
<ExperienceCard
|
|
||||||
key={index}
|
|
||||||
experience={exp}
|
|
||||||
onClick={() => setSelectedExperience(exp)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-center text-gray-400 mt-4">
|
|
||||||
And a bunch of stuff i probably also forgot...
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<InternshipSection />
|
|
||||||
|
|
||||||
<Contact />
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* Floating Island: Bottom Left (only on localhost) */}
|
|
||||||
{typeof window !== "undefined" &&
|
|
||||||
(window.location.hostname === "localhost" ||
|
|
||||||
window.location.hostname === "127.0.0.1") && (
|
|
||||||
<div
|
|
||||||
className="fixed bottom-4 left-4 z-50 flex flex-col items-start gap-2 bg-gradient-to-br from-purple-900/80 to-black/80 border border-purple-500/30 rounded-2xl shadow-lg px-5 py-3 text-sm animate-fade-in"
|
|
||||||
style={{ minWidth: 180 }}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span role="img" aria-label="island">
|
|
||||||
🏝️
|
|
||||||
</span>
|
|
||||||
<span className="font-semibold text-purple-300">Loaded</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-1 mt-1">
|
|
||||||
<span className="text-white/90">
|
|
||||||
Projects: {projects.length}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/90">
|
|
||||||
Mini Projects: {miniProjects.length}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/90">
|
|
||||||
Experiences: {experiences.length}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
src/components/Navbar.tsx
Normal file
38
src/components/Navbar.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { useUmami } from "@danielgtmn/umami-react";
|
||||||
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
export function Navbar() {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ label: "Home", path: "/" },
|
||||||
|
{ label: "About", path: "/about" },
|
||||||
|
{ label: "Connect", path: "/contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleSwitch = (path: string) => {
|
||||||
|
useUmami().track("nav_to_" + path.replaceAll("/", "_"));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="fixed top-8 left-1/2 -translate-x-1/2 z-[100] w-[min(90%,400px)]">
|
||||||
|
<div className="bg-black/20 backdrop-blur-xl border border-white/10 rounded-full px-6 py-3 flex items-center justify-between gap-4 shadow-2xl">
|
||||||
|
{navItems.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.path}
|
||||||
|
to={item.path}
|
||||||
|
onClick={() => handleSwitch(item.path)}
|
||||||
|
className={`text-sm font-semibold transition-all duration-300 px-4 py-2 rounded-full
|
||||||
|
${
|
||||||
|
location.pathname === item.path
|
||||||
|
? "bg-white text-black scale-105 shadow-xl shadow-white/10"
|
||||||
|
: "text-gray-400 hover:text-white"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
39
src/pages/AboutPage.tsx
Normal file
39
src/pages/AboutPage.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { About as AboutSection } from "../sections/About";
|
||||||
|
import { Goals } from "../sections/Goals";
|
||||||
|
|
||||||
|
export function About() {
|
||||||
|
return (
|
||||||
|
<div className="space-y-24 py-12 max-w-4xl mx-auto px-4">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<h1 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-500">
|
||||||
|
About Me
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||||
|
A little bit about me...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AboutSection />
|
||||||
|
|
||||||
|
<Goals />
|
||||||
|
|
||||||
|
<section className="bg-white/5 border border-white/10 rounded-3xl p-8 backdrop-blur-sm">
|
||||||
|
<h3 className="text-2xl font-bold mb-4">Interests Outside of Coding</h3>
|
||||||
|
<ul className="grid grid-cols-1 md:grid-cols-2 gap-4 text-gray-300">
|
||||||
|
<li className="flex items-center gap-2">
|
||||||
|
<span className="text-purple-400">✦</span> 3D Printing & Design
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-2">
|
||||||
|
<span className="text-purple-400">✦</span> Graphical Design
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-2">
|
||||||
|
<span className="text-purple-400">✦</span> Gaming & Hardware
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center gap-2">
|
||||||
|
<span className="text-purple-400">✦</span> Open Source Contribution
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
src/pages/ContactPage.tsx
Normal file
38
src/pages/ContactPage.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Contact as ContactSection } from "../sections/Contact";
|
||||||
|
|
||||||
|
export function Contact() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[70vh] px-4 space-y-12 w-full max-w-4xl mx-auto">
|
||||||
|
<div className="text-center space-y-4 w-full">
|
||||||
|
<h1 className="text-5xl md:text-6xl font-extrabold text-white">
|
||||||
|
Let's <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-500">Connect</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-400 text-xl max-w-xl mx-auto">
|
||||||
|
Whatever you've got in mind, I'm just a few clicks away.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full max-w-3xl">
|
||||||
|
<ContactSection />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 w-full max-w-4xl text-center">
|
||||||
|
<div className="bg-white/5 border border-white/10 p-6 rounded-3xl backdrop-blur-sm group hover:border-blue-500/30 transition-all duration-300">
|
||||||
|
<div className="text-3xl mb-4 group-hover:scale-110 transition-transform">📧</div>
|
||||||
|
<h3 className="font-bold mb-1">Email</h3>
|
||||||
|
<p className="text-sm text-gray-500">space@reversed.dev</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/5 border border-white/10 p-6 rounded-3xl backdrop-blur-sm group hover:border-purple-500/30 transition-all duration-300">
|
||||||
|
<div className="text-3xl mb-4 group-hover:scale-110 transition-transform">👾</div>
|
||||||
|
<h3 className="font-bold mb-1">Discord</h3>
|
||||||
|
<p className="text-sm text-gray-500">@getspaced</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/5 border border-white/10 p-6 rounded-3xl backdrop-blur-sm group hover:border-pink-500/30 transition-all duration-300">
|
||||||
|
<div className="text-3xl mb-4 group-hover:scale-110 transition-transform">💻</div>
|
||||||
|
<h3 className="font-bold mb-1">GitHub</h3>
|
||||||
|
<p className="text-sm text-gray-500">github.com/Space-Banane</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
151
src/pages/Home.tsx
Normal file
151
src/pages/Home.tsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { Hero } from "../sections/Hero";
|
||||||
|
import type { MiniProject, Experience, Project } from "../types";
|
||||||
|
import { ProjectCard } from "../components/ProjectCard";
|
||||||
|
import { MiniProjectCard } from "../components/MiniProjectCard";
|
||||||
|
import { ExperienceCard } from "../components/ExperienceCard";
|
||||||
|
|
||||||
|
interface HomeProps {
|
||||||
|
glowColor: string;
|
||||||
|
borderStatus: string;
|
||||||
|
displayMessage: string;
|
||||||
|
rotatingMessages: string[];
|
||||||
|
statusMessage: string;
|
||||||
|
showOldNames: boolean;
|
||||||
|
setShowOldNames: (show: boolean) => void;
|
||||||
|
oldUsernames: string[];
|
||||||
|
projects: Project[];
|
||||||
|
miniProjects: MiniProject[];
|
||||||
|
setSelectedMiniProject: (p: MiniProject) => void;
|
||||||
|
experiences: Experience[];
|
||||||
|
setSelectedExperience: (e: Experience) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Home({
|
||||||
|
glowColor,
|
||||||
|
borderStatus,
|
||||||
|
displayMessage,
|
||||||
|
rotatingMessages,
|
||||||
|
statusMessage,
|
||||||
|
showOldNames,
|
||||||
|
setShowOldNames,
|
||||||
|
oldUsernames,
|
||||||
|
projects,
|
||||||
|
miniProjects,
|
||||||
|
setSelectedMiniProject,
|
||||||
|
experiences,
|
||||||
|
setSelectedExperience,
|
||||||
|
}: HomeProps) {
|
||||||
|
const groupedExperiences = experiences.reduce(
|
||||||
|
(acc, exp) => {
|
||||||
|
if (!acc[exp.type]) {
|
||||||
|
acc[exp.type] = [];
|
||||||
|
}
|
||||||
|
acc[exp.type].push(exp);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, Experience[]>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const rewriteType = (type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case "language":
|
||||||
|
return "Languages";
|
||||||
|
case "service":
|
||||||
|
return "Services";
|
||||||
|
case "platform":
|
||||||
|
return "Platforms";
|
||||||
|
case "real life experience":
|
||||||
|
return "Real Life Experience";
|
||||||
|
case "roles":
|
||||||
|
return "Roles";
|
||||||
|
default:
|
||||||
|
return type.charAt(0).toUpperCase() + type.slice(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-24 pb-20">
|
||||||
|
<Hero
|
||||||
|
glowColor={glowColor}
|
||||||
|
borderStatus={borderStatus}
|
||||||
|
displayMessage={displayMessage}
|
||||||
|
rotatingMessages={rotatingMessages}
|
||||||
|
statusMessage={statusMessage}
|
||||||
|
showOldNames={showOldNames}
|
||||||
|
setShowOldNames={setShowOldNames}
|
||||||
|
oldUsernames={oldUsernames}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="w-full max-w-6xl space-y-12">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-500">
|
||||||
|
Featured Projects
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||||
|
A selection of my personal favorites. Many more on my GitHub.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 px-4">
|
||||||
|
{projects.map((project, index) => (
|
||||||
|
<ProjectCard key={index} project={project} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="w-full max-w-6xl space-y-12">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-500">
|
||||||
|
Mini Projects
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||||
|
Small tools, experiments, and fun little things I've built.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4 px-4">
|
||||||
|
{miniProjects.map((mini, index) => (
|
||||||
|
<MiniProjectCard
|
||||||
|
key={index}
|
||||||
|
project={mini}
|
||||||
|
onClick={() => setSelectedMiniProject(mini)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="w-full max-w-6xl space-y-16">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<h2 className="text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-blue-500">
|
||||||
|
Experience
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-400 max-w-2xl mx-auto">
|
||||||
|
My journey through the tech world so far.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-12 px-4">
|
||||||
|
{Object.entries(groupedExperiences)
|
||||||
|
.sort(([typeA], [typeB]) => typeA.localeCompare(typeB))
|
||||||
|
.map(([type, items]) => (
|
||||||
|
<div key={type} className="space-y-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<h3 className="text-xl font-semibold text-gray-200 capitalize whitespace-nowrap">
|
||||||
|
{rewriteType(type)}
|
||||||
|
</h3>
|
||||||
|
<div className="h-px w-full bg-gradient-to-r from-gray-500/20 via-gray-500/10 to-transparent" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{items.map((exp, index) => (
|
||||||
|
<ExperienceCard
|
||||||
|
key={index}
|
||||||
|
experience={exp}
|
||||||
|
onClick={() => setSelectedExperience(exp)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export function Contact() {
|
export function Contact() {
|
||||||
return (
|
return (
|
||||||
<section className="w-full max-w-2xl text-center space-y-4 pb-8">
|
<section className="w-full max-w-2xl text-center space-y-4 pb-8 mx-auto">
|
||||||
<h2 className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500">
|
<h2 className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-green-400 via-blue-500 to-purple-500">
|
||||||
Want to talk?
|
Want to talk?
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,38 +1,90 @@
|
|||||||
|
import { useUmami } from "@danielgtmn/umami-react";
|
||||||
|
import { Github, Mail, MessageSquare } from "lucide-react";
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
|
const handleLinkClick = (name: string, url: string) => {
|
||||||
|
useUmami().track(`footer_click_${name.toLowerCase()}`, { url });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="w-full border-t border-white/10 pt-4">
|
<footer className="w-full border-t border-white/5 py-8 mt-20">
|
||||||
<div className="text-center space-y-3">
|
<div className="max-w-7xl mx-auto px-6 flex flex-col items-center gap-6">
|
||||||
<div className="flex items-center justify-center gap-2">
|
{/* Social Icons Row */}
|
||||||
<img
|
<div className="flex items-center gap-6 text-gray-400">
|
||||||
src="http://shsf.reversed.dev/SHSF%20SMALL%20TRANSPARENT.png"
|
<a
|
||||||
alt="SHSF Logo"
|
href="https://github.com/Space-Banane"
|
||||||
className="w-6 h-6"
|
target="_blank"
|
||||||
/>
|
rel="noreferrer"
|
||||||
<p className="text-gray-400 text-sm">
|
className="hover:text-purple-400 transition-colors"
|
||||||
Partly powered by{" "}
|
aria-label="GitHub"
|
||||||
|
onClick={() =>
|
||||||
|
handleLinkClick("GitHub", "https://github.com/Space-Banane")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Github className="w-5 h-5" />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="mailto:space@reversed.dev"
|
||||||
|
className="hover:text-purple-400 transition-colors"
|
||||||
|
aria-label="Email"
|
||||||
|
onClick={() =>
|
||||||
|
handleLinkClick("Email", "mailto:space@reversed.dev")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Mail className="w-5 h-5" />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://discord.com/users/456443941169004545"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="hover:text-purple-400 transition-colors"
|
||||||
|
aria-label="Discord"
|
||||||
|
onClick={() =>
|
||||||
|
handleLinkClick(
|
||||||
|
"Discord",
|
||||||
|
"https://discord.com/users/456443941169004545",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MessageSquare className="w-5 h-5" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Branding & Attribution */}
|
||||||
|
<div className="flex flex-col items-center gap-2 text-center">
|
||||||
|
<p className="text-gray-500 text-xs tracking-wide">
|
||||||
|
© {currentYear} • Space-Banane • Love from Space
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-3 text-[10px] text-gray-600 uppercase tracking-[0.2em]">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/Space-Banane/shsf"
|
href="https://github.com/Space-Banane/shsf"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="text-purple-400 hover:text-purple-300 transition-colors"
|
className="hover:text-gray-400 transition-colors"
|
||||||
|
onClick={() =>
|
||||||
|
handleLinkClick("SHSF", "https://github.com/Space-Banane/shsf")
|
||||||
|
}
|
||||||
>
|
>
|
||||||
SHSF
|
SHSF
|
||||||
</a>
|
</a>
|
||||||
</p>
|
<span>•</span>
|
||||||
|
<a
|
||||||
|
href="https://gradienty.codes/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="hover:text-gray-400 transition-colors"
|
||||||
|
onClick={() =>
|
||||||
|
handleLinkClick("Gradienty", "https://gradienty.codes/")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Gradienty
|
||||||
|
</a>
|
||||||
|
<span>•</span>
|
||||||
|
<span>React</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-400 text-sm">Love from Space ❤️</p>
|
|
||||||
<p className="text-gray-500 text-xs">
|
|
||||||
Designed with Copilot &{" "}
|
|
||||||
<a
|
|
||||||
href="https://gradienty.codes/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="text-purple-400 hover:text-purple-300 transition-colors underline"
|
|
||||||
>
|
|
||||||
gradienty.codes
|
|
||||||
</a>
|
|
||||||
, Tailwind and React
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -69,6 +69,22 @@ export function Goals() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="md:col-span-2 p-6 rounded-2xl bg-gradient-to-br from-yellow-500/10 to-orange-500/5 backdrop-blur-sm border border-yellow-500/20">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="p-3 rounded-lg bg-yellow-500/20 text-3xl">
|
||||||
|
🏠
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-bold text-white mb-2">
|
||||||
|
Embrace Self-Hosting
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 leading-relaxed">
|
||||||
|
I want to learn more about self-hosting my own services and reduce reliance on cloud providers for personal projects.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
export function InternshipSection() {
|
|
||||||
return (
|
|
||||||
<section className="w-full space-y-8">
|
|
||||||
<div className="text-center space-y-2">
|
|
||||||
<h2 className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-orange-500">
|
|
||||||
Real Life Experience (ABB Internship)
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
|
||||||
My internship experience at ABB and what i learned from it
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-3xl mx-auto">
|
|
||||||
<div className="p-6 rounded-2xl bg-gradient-to-br from-yellow-500/10 to-orange-500/5 backdrop-blur-sm border border-yellow-500/20">
|
|
||||||
<div className="space-y-4 text-gray-300">
|
|
||||||
<p className="leading-relaxed">
|
|
||||||
I did an internship at{" "}
|
|
||||||
<a
|
|
||||||
href="https://www.abb.com/global/en"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="text-red-500 hover:text-red-600 transition-colors font-semibold"
|
|
||||||
>
|
|
||||||
ABB{" "}
|
|
||||||
</a>
|
|
||||||
where i got to work on a project that involved internal management
|
|
||||||
software. It was a great experience that taught me a lot about
|
|
||||||
working in a professional environment and collaborating with a
|
|
||||||
team of developers.
|
|
||||||
</p>
|
|
||||||
<p className="leading-relaxed">
|
|
||||||
This was the first propper time i worked with a Framework (Angular) and it was a great learning experience. Which helped me understand why
|
|
||||||
frameworks are a thing and how they can help structure and organize codebases, especially as they grow larger.
|
|
||||||
</p>
|
|
||||||
<p className="leading-relaxed">
|
|
||||||
Also, this was the first time submitting a PR, which was scary but super rewarding when they merged it. From that point on
|
|
||||||
i was hooked on Frameworks and OSS contribution. I want to do more of both in the future.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user