The rail and the "next, you are wondering" links are gone (owner: flat, and the question stood twice in a row). The page now reads from top to bottom: Every chapter opens with one sentence, and the sentences follow each other as the way of a project: you need someone who builds it, first we find out what you need, sometimes it is less work by hand, sometimes a meeting, then it always runs the same way, in the end something runs, a few questions remain, and it starts with an email. One line runs down the whole page and draws itself as the visitor scrolls. Under each sentence it crosses the page, stands a figure on the line (diamond, hexagon, screen, circle, square, triangle) and continues down the other side; a glowing dot travels at its head and comes to rest in front of the last sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.5 KiB
TypeScript
88 lines
3.5 KiB
TypeScript
"use client";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import * as React from "react";
|
|
|
|
import { pathFor } from "@/src/domain/routes";
|
|
import IconWhite from "../assets/logo/Icon-White-Transparent.svg";
|
|
import { DecodeText } from "./bits/DecodeText";
|
|
import { ScrollBits } from "./bits/ScrollBits";
|
|
import { useContent, useLocale } from "./ContentProvider";
|
|
import { LanguageSwitch } from "./LanguageSwitch";
|
|
|
|
/** Header: the logo, the link to the freelance page, the language switch and one action. */
|
|
export const Header: React.FC = () => {
|
|
const { nav } = useContent();
|
|
const locale = useLocale();
|
|
const home = pathFor("home", locale);
|
|
const freelance = pathFor("freelance", locale);
|
|
const [isScrolled, setIsScrolled] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 20);
|
|
};
|
|
window.addEventListener("scroll", handleScroll);
|
|
return () => window.removeEventListener("scroll", handleScroll);
|
|
}, []);
|
|
|
|
return (
|
|
<header className="sticky top-0 z-[100] w-full">
|
|
{/* Decoupled Background Layer - Prevents backdrop-filter parent context bugs */}
|
|
<div
|
|
className={`absolute inset-0 transition-all duration-500 -z-10 ${
|
|
isScrolled
|
|
? "bg-white/70 backdrop-blur-xl border-b border-slate-100 shadow-sm shadow-slate-100/50"
|
|
: "bg-white/80 backdrop-blur-md border-b border-slate-50"
|
|
}`}
|
|
/>
|
|
|
|
<div className="mx-auto w-full max-w-[1400px] px-5 md:px-12 py-4 flex items-center justify-between relative z-10">
|
|
<Link
|
|
href={home}
|
|
aria-label={nav.logoLabel}
|
|
onClick={(event) => {
|
|
// Already on the homepage: scroll up instead of reloading the route.
|
|
if (window.location.pathname === home) {
|
|
event.preventDefault();
|
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
}
|
|
}}
|
|
className="flex items-center gap-4 group"
|
|
>
|
|
<div className="w-10 h-10 md:w-12 md:h-12 bg-black rounded-xl flex items-center justify-center group-hover:scale-105 transition-all duration-500 shadow-sm shrink-0 relative overflow-hidden">
|
|
<Image
|
|
src={IconWhite}
|
|
alt=""
|
|
width={32}
|
|
height={32}
|
|
className="w-6 h-6 md:w-8 md:h-8 relative z-10"
|
|
/>
|
|
</div>
|
|
</Link>
|
|
|
|
<ScrollBits className="hidden font-mono text-[10px] tracking-[0.3em] text-slate-400 md:block" />
|
|
|
|
<div className="flex items-center gap-2.5 md:gap-4">
|
|
<Link
|
|
href={freelance}
|
|
className="hidden font-sans text-[10px] font-bold uppercase tracking-[0.2em] text-slate-500 no-underline transition-colors duration-300 hover:text-slate-900 md:block"
|
|
>
|
|
{nav.freelance}
|
|
</Link>
|
|
<LanguageSwitch />
|
|
<Link
|
|
href={`${home}#contact`}
|
|
className="bit-cta bit-cta-quiet font-sans text-[10px] font-bold uppercase tracking-[0.2em] text-slate-900 border border-slate-200 px-5 py-2.5 rounded-full no-underline hover:border-slate-400 hover:bg-slate-50 transition-all duration-500 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-slate-100"
|
|
style={{
|
|
transitionTimingFunction: "cubic-bezier(0.23, 1, 0.32, 1)",
|
|
}}
|
|
>
|
|
<DecodeText text={nav.contact} trigger="hover" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|