aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorschererleander <leander@schererleander.de>2025-12-25 15:35:32 +0000
committerschererleander <leander@schererleander.de>2025-12-25 15:35:32 +0000
commit56c7d55f0bf3e41e47191b00672407fa75a6dd64 (patch)
tree76e39fda619a202fa6029458a3966ab1b1361214 /src
parent1f0c3ae8580268ea228d1030839b558ee4d5e710 (diff)
add hero section map
Diffstat (limited to 'src')
-rw-r--r--src/pages/Home.tsx195
1 files changed, 94 insertions, 101 deletions
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index cb3320b..de32cda 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -1,115 +1,108 @@
+import { useEffect, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
-import {
- Card,
- CardTitle,
- CardDescription,
- CardContent,
-} from "@/components/ui/card";
+import { Card, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
import { Projects } from "@/data/projects";
import { Tools } from "@/data/tools";
import { Github, Mail } from "lucide-react";
+import { MapContainer, TileLayer, Marker, useMap } from "react-leaflet";
+import L from "leaflet";
+import "leaflet/dist/leaflet.css";
+
+const KARLSRUHE: [number, number] = [49.0069, 8.4037];
+
+const icon = L.divIcon({
+ html: `<div style="width:12px;height:12px;background:#fff;border-radius:50%;border:2px solid #000;box-shadow:0 0 10px rgba(255,255,255,0.5)"></div>`,
+ className: "",
+ iconSize: [12, 12],
+});
+
+function MapEffects() {
+ const map = useMap();
+ useEffect(() => {
+ map.flyTo(KARLSRUHE, 14, { duration: 3 });
+ }, [map]);
+ return null;
+}
+
export default function Home() {
+ const [mounted, setMounted] = useState(false);
+ const [isDark, setIsDark] = useState(false);
+
+ useEffect(() => {
+ setMounted(true);
+ const checkDark = () => setIsDark(document.documentElement.classList.contains("dark"));
+ checkDark();
+ const obs = new MutationObserver(checkDark);
+ obs.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
+ return () => obs.disconnect();
+ }, []);
+
+ if (!mounted) return null;
+
+ const tileUrl = isDark
+ ? "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
+ : "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png";
+
return (
- <>
- <title>߸ home</title>
- <main className="container mx-auto py-12">
- <div className="relative flex justify-center items-center pt-24">
- {/* Gradient Blob */}
- <div
- aria-hidden="true"
- className="absolute left-1/2 -translate-x-1/2 w-[32rem] h-[32rem] bg-gradient-to-tr from-blue-400 via-purple-300 to-pink-300 opacity-60 blur-3xl rounded-full z-0 dark:from-blue-400 dark:via-purple-400 dark:to-pink-400 dark:opacity-30"
- style={{ pointerEvents: "none" }}
- />
- <section className="text-center relative z-10">
- <h1 className="text-4xl font-bold mb-4">
- Hi,{" "}
- <span className="text-blue-500 dark:text-purple-500">
- I'm Leander.
- </span>
- </h1>
- <p className="text-lg max-w-2xl mx-auto">
- Passionate about hardware & software, pursuing computer science
- studies. Currently building 3D-printing projects and exploring
- homelabing.
- </p>
- <div className="flex justify-center gap-4">
- <Button asChild className="mt-4">
- <a
- href="https://github.com/schererleander"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Github /> Github
- </a>
- </Button>
- <Button asChild className="mt-4 bg-blue-500 dark:bg-purple-500">
- <a
- href="mailto:leander@schererleander.de"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Mail /> Mail
- </a>
- </Button>
- </div>
- </section>
+ <main className="container mx-auto py-12">
+ <section className="relative">
+ <div
+ className="relative h-[450px] w-full overflow-hidden rounded-xl border border-border z-0"
+ style={{
+ maskImage: "linear-gradient(to bottom, black 50%, transparent 100%)",
+ WebkitMaskImage: "linear-gradient(to bottom, black 50%, transparent 100%)"
+ }}
+ >
+ <MapContainer center={[49, 8.4]} zoom={10} scrollWheelZoom={false} className="h-full w-full" zoomControl={false} attributionControl={false}>
+ <TileLayer key={tileUrl} url={tileUrl} />
+ <Marker position={KARLSRUHE} icon={icon} />
+ <MapEffects />
+ </MapContainer>
</div>
- <section className="mt-12">
- <h2 className="text-2xl font-semibold mb-4">Interests</h2>
- <ul className="list-disc pl-6 space-y-2">
- <li>*nix systems</li>
- <li>3D printing</li>
- <li>Homelab & self-hosting</li>
- </ul>
- </section>
- <section className="mt-12">
- <h2 className="text-2xl font-semibold mb-6">Tools & Technologies</h2>
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
- {Tools.map((tool) => (
- <Card className="bg-secondary border border-border p-2">
- <div className="flex items-center px-4 py-2">
- <div className={`p-2 rounded-sm ${tool.color}`}>
- <img src={tool.image} alt={tool.name} className="w-6 h-6" />
- </div>
- <div className="ml-4">
- <CardTitle>{tool.name}</CardTitle>
- <CardDescription>{tool.description}</CardDescription>
- </div>
- </div>
- </Card>
- ))}
+ <div className="relative -mt-32 z-10 text-center">
+ <h1 className="text-4xl font-bold mb-4">Hi, <span className="text-blue-500 dark:text-purple-500">I'm Leander.</span></h1>
+ <p className="text-lg max-w-2xl mx-auto mb-4">Passionate about hardware & software, pursuing computer science studies. Currently building 3D-printing projects and exploring homelabing.</p>
+ <div className="flex justify-center gap-4">
+ <Button asChild className="mt-4"><a href="https://github.com/schererleander" target="_blank" rel="noopener noreferrer"><Github className="mr-2 h-4 w-4" /> Github</a></Button>
+ <Button asChild className="mt-4 bg-blue-500 dark:bg-purple-500"><a href="mailto:leander@schererleander.de" target="_blank" rel="noopener noreferrer"><Mail className="mr-2 h-4 w-4" /> Mail</a></Button>
</div>
- </section>
+ </div>
+ </section>
- <section className="mt-12">
- <h2 className="text-2xl font-semibold mb-6">Projects</h2>
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
- {Projects.map((project) => (
- <a href={project.link} target="_blank" rel="noopener noreferrer">
- <Card className="bg-secondary border border-border py-2">
- <CardContent>
- <img
- src={project.image}
- alt={project.name}
- className="w-64 h-64 rounded-lg mb-4 object-contain mx-auto"
- />
- <CardTitle>{project.name}</CardTitle>
- <CardDescription>{project.description}</CardDescription>
- <div className="flex flex-wrap gap-2 my-4">
- {project.badges.map((badge) => (
- <Badge key={badge}>{badge}</Badge>
- ))}
- </div>
- </CardContent>
- </Card>
- </a>
- ))}
- </div>
- </section>
- </main>
- </>
+ <section className="mt-24">
+ <h2 className="text-2xl font-semibold mb-6">Tools & Technologies</h2>
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+ {Tools.map((t, i) => (
+ <Card key={i} className="bg-secondary border border-border p-2">
+ <div className="flex items-center px-4 py-2">
+ <div className={`p-2 rounded-sm ${t.color}`}><img src={t.image} alt={t.name} className="w-6 h-6" /></div>
+ <div className="ml-4"><CardTitle>{t.name}</CardTitle><CardDescription>{t.description}</CardDescription></div>
+ </div>
+ </Card>
+ ))}
+ </div>
+ </section>
+
+ <section className="mt-24">
+ <h2 className="text-2xl font-semibold mb-6">Recent Projects</h2>
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+ {Projects.map((p, i) => (
+ <a key={i} href={p.link} target="_blank" rel="noopener noreferrer">
+ <Card className="bg-secondary border border-border py-2 h-full">
+ <CardContent>
+ <img src={p.image} alt={p.name} className="w-full h-48 rounded-lg mb-4 object-cover mx-auto" />
+ <CardTitle>{p.name}</CardTitle>
+ <CardDescription>{p.description}</CardDescription>
+ <div className="flex flex-wrap gap-2 my-4">{p.badges.map(b => <Badge key={b}>{b}</Badge>)}</div>
+ </CardContent>
+ </Card>
+ </a>
+ ))}
+ </div>
+ </section>
+ </main>
);
}