Refactored About section to use a responsive Bento Grid layout. Redesigned Hero for stronger visual impact. Implemented floating Island navigation. Updated Project cards for cleaner aesthetic.
108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import { Menu, X } from "lucide-react";
|
|
import Link from "next/link";
|
|
import { useLocale, useTranslations } from "next-intl";
|
|
import { usePathname } from "next/navigation";
|
|
import { ThemeToggle } from "./ThemeToggle";
|
|
|
|
const Header = () => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const locale = useLocale();
|
|
const pathname = usePathname();
|
|
const t = useTranslations("nav");
|
|
|
|
const isHome = pathname === `/${locale}` || pathname === `/${locale}/`;
|
|
|
|
const navItems = [
|
|
{ name: t("home"), href: `/${locale}` },
|
|
{ name: t("about"), href: isHome ? "#about" : `/${locale}#about` },
|
|
{ name: t("projects"), href: isHome ? "#projects" : `/${locale}/projects` },
|
|
{ name: t("contact"), href: isHome ? "#contact" : `/${locale}#contact` },
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<div className="fixed top-6 left-0 right-0 z-50 flex justify-center px-4 pointer-events-none">
|
|
<motion.nav
|
|
initial={{ y: -100, opacity: 0 }}
|
|
animate={{ y: 0, opacity: 1 }}
|
|
transition={{ duration: 0.5, ease: "easeOut" }}
|
|
className="pointer-events-auto bg-white/80 dark:bg-stone-900/80 backdrop-blur-xl border border-stone-200/50 dark:border-stone-800/50 shadow-lg rounded-full px-2 py-2 flex items-center gap-2"
|
|
>
|
|
{/* Logo / Home Button */}
|
|
<Link
|
|
href={`/${locale}`}
|
|
className="w-10 h-10 flex items-center justify-center rounded-full bg-stone-100 dark:bg-stone-800 hover:bg-stone-200 dark:hover:bg-stone-700 transition-colors font-bold text-stone-900 dark:text-stone-100"
|
|
>
|
|
dk
|
|
</Link>
|
|
|
|
{/* Desktop Nav Items */}
|
|
<div className="hidden md:flex items-center gap-1 px-2">
|
|
{navItems.map((item) => (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
className="px-4 py-2 text-sm font-medium text-stone-600 dark:text-stone-400 hover:text-stone-900 dark:hover:text-stone-100 hover:bg-stone-100 dark:hover:bg-stone-800/50 rounded-full transition-all"
|
|
>
|
|
{item.name}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
<div className="w-px h-6 bg-stone-200 dark:bg-stone-800 mx-1 hidden md:block"></div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex items-center gap-1">
|
|
<Link
|
|
href={locale === "en" ? pathname.replace(/^\/en/, "/de") : pathname.replace(/^\/de/, "/en")}
|
|
className="w-9 h-9 flex items-center justify-center text-xs font-bold text-stone-600 dark:text-stone-400 hover:bg-stone-100 dark:hover:bg-stone-800 rounded-full transition-colors"
|
|
>
|
|
{locale === "en" ? "DE" : "EN"}
|
|
</Link>
|
|
<ThemeToggle />
|
|
|
|
{/* Mobile Menu Button */}
|
|
<button
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className="w-9 h-9 flex md:hidden items-center justify-center text-stone-600 dark:text-stone-400 hover:bg-stone-100 dark:hover:bg-stone-800 rounded-full transition-colors"
|
|
>
|
|
{isOpen ? <X size={18} /> : <Menu size={18} />}
|
|
</button>
|
|
</div>
|
|
</motion.nav>
|
|
</div>
|
|
|
|
{/* Mobile Menu Overlay */}
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95, y: -20 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.95, y: -20 }}
|
|
className="fixed top-20 left-4 right-4 z-40 bg-white dark:bg-stone-900 border border-stone-200 dark:border-stone-800 rounded-3xl shadow-2xl p-4 md:hidden"
|
|
>
|
|
<div className="flex flex-col gap-2">
|
|
{navItems.map((item) => (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
onClick={() => setIsOpen(false)}
|
|
className="px-4 py-3 text-lg font-medium text-stone-900 dark:text-stone-100 bg-stone-50 dark:bg-stone-800/50 rounded-xl"
|
|
>
|
|
{item.name}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Header;
|