Files
mintel.me/apps/web/src/components/Header.tsx
T
mmintelandClaude Opus 5.5 90120b60db feat: the homepage leads the visitor through as one story
The page was a row of sections to scroll past, and a film in one of them did
not change that. It now follows the questions a visitor has, in the order
they come up: who is this, can he do what I need, what does it look like,
what else, how does it go, has he done it, what is open, how do I start.

Each chapter ends by asking the next question as a large link that leads on,
and the next chapter answers it. A rail at the edge shows every chapter as a
station with a dot travelling along it (on phones: a pill with the current
chapter and one tap to the next), and the header names the current question.

Where the visitor is, is pure logic in src/domain/chapters.ts. The scroll
film section and the bridge sentences are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 10:24:52 +02:00

93 lines
3.6 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";
import { ChapterNow } from "./story/StoryGuide";
/** 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>
<ChapterNow
fallback={
<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>
);
};