149 lines
6.0 KiB
TypeScript
149 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import { type KeyboardEvent, useEffect, useRef, useState } from "react";
|
|
import { NAVIGATION_ITEMS } from "@/lib/site-navigation";
|
|
import MagneticButton from "./cinematic/MagneticButton";
|
|
import { Logo } from "./Logo";
|
|
|
|
const MENU_ID = "site-navigation-menu";
|
|
|
|
export function Header() {
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
|
|
|
useEffect(() => {
|
|
let wasScrolled = false;
|
|
const updateScrolled = () => {
|
|
const nextScrolled = window.scrollY > 0;
|
|
if (nextScrolled === wasScrolled) return;
|
|
wasScrolled = nextScrolled;
|
|
setScrolled(nextScrolled);
|
|
};
|
|
|
|
updateScrolled();
|
|
window.addEventListener("scroll", updateScrolled, { passive: true });
|
|
return () => window.removeEventListener("scroll", updateScrolled);
|
|
}, []);
|
|
|
|
const closeMobileMenu = () => setMobileMenuOpen(false);
|
|
|
|
const handleMenuKeyDown = (event: KeyboardEvent<HTMLElement>) => {
|
|
if (event.key !== "Escape") return;
|
|
|
|
event.preventDefault();
|
|
closeMobileMenu();
|
|
menuButtonRef.current?.focus();
|
|
};
|
|
|
|
return (
|
|
<header
|
|
className={`fixed inset-x-0 top-0 z-[9000] h-[var(--site-header-height)] w-full border-b px-sp-6 text-[var(--white)] transition-colors duration-200 lg:px-sp-8 xl:px-sp-12 ${
|
|
scrolled
|
|
? "border-white/12 bg-[var(--ink)]/95 backdrop-blur-md"
|
|
: "border-transparent bg-[var(--ink)]"
|
|
}`}
|
|
>
|
|
<div className="ds-section-shell flex h-full items-center justify-between gap-sp-6">
|
|
<a
|
|
href="/#home"
|
|
aria-label="Evandro.dev.br — início"
|
|
className="flex min-h-11 shrink-0 items-center no-underline"
|
|
>
|
|
<Logo theme="dark" loading="eager" className="w-40 sm:w-44" />
|
|
</a>
|
|
|
|
<nav
|
|
aria-label="Navegação principal"
|
|
className="hidden items-center xl:flex"
|
|
>
|
|
<ul className="flex list-none items-center gap-sp-4">
|
|
{NAVIGATION_ITEMS.map((item) => (
|
|
<li key={item.href}>
|
|
<a
|
|
href={item.href}
|
|
className="inline-flex min-h-11 items-center whitespace-nowrap rounded-sm px-sp-3 font-display text-sm font-medium normal-case tracking-normal text-white/80 no-underline transition-colors hover:bg-white/5 hover:text-white"
|
|
>
|
|
{item.label}
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</nav>
|
|
|
|
<div className="flex shrink-0 items-center">
|
|
<div className="hidden xl:block">
|
|
<a
|
|
href="/#contact"
|
|
className="inline-flex min-h-11 items-center whitespace-nowrap rounded-sm bg-[var(--slash)] px-sp-4 font-display text-sm font-semibold normal-case tracking-normal text-[var(--ink)] no-underline transition-colors hover:bg-[var(--white)]"
|
|
>
|
|
Falar sobre meu projeto
|
|
</a>
|
|
</div>
|
|
<button
|
|
ref={menuButtonRef}
|
|
type="button"
|
|
className="flex h-11 w-11 flex-col items-center justify-center gap-sp-2 border-none bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)] xl:hidden"
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
aria-controls={mobileMenuOpen ? MENU_ID : undefined}
|
|
aria-label={mobileMenuOpen ? "Fechar menu" : "Abrir menu"}
|
|
aria-expanded={mobileMenuOpen}
|
|
onKeyDown={handleMenuKeyDown}
|
|
>
|
|
<span
|
|
className={`h-0.5 w-5 bg-white transition-transform duration-300 ${mobileMenuOpen ? "translate-y-[6px] rotate-45" : ""}`}
|
|
/>
|
|
<span
|
|
className={`h-0.5 w-5 bg-white transition-opacity duration-300 ${mobileMenuOpen ? "opacity-0" : ""}`}
|
|
/>
|
|
<span
|
|
className={`h-0.5 w-5 bg-white transition-transform duration-300 ${mobileMenuOpen ? "-translate-y-[6px] -rotate-45" : ""}`}
|
|
/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{mobileMenuOpen ? (
|
|
<div className="absolute inset-x-0 top-[var(--site-header-height)] max-h-[calc(100dvh-var(--site-header-height))] overflow-y-auto border-b border-white/12 bg-[var(--ink)] px-sp-6 lg:px-sp-8 xl:hidden">
|
|
<div className="ds-section-shell flex flex-col py-sp-6">
|
|
<nav
|
|
id={MENU_ID}
|
|
aria-hidden={!mobileMenuOpen}
|
|
aria-label="Navegação mobile"
|
|
onKeyDown={handleMenuKeyDown}
|
|
>
|
|
<ul className="flex list-none flex-col gap-sp-2">
|
|
{NAVIGATION_ITEMS.map((item) => (
|
|
<li key={item.href}>
|
|
<MagneticButton
|
|
as="a"
|
|
strength={0}
|
|
wrapperClassName="w-full"
|
|
href={item.href}
|
|
className="flex min-h-11 items-center rounded-sm px-sp-3 py-sp-3 font-display text-sm font-medium normal-case tracking-normal text-white/80 no-underline transition-colors hover:bg-white/5 hover:text-white"
|
|
onClick={closeMobileMenu}
|
|
>
|
|
{item.label}
|
|
</MagneticButton>
|
|
</li>
|
|
))}
|
|
<li>
|
|
<MagneticButton
|
|
as="a"
|
|
strength={0}
|
|
href="/#contact"
|
|
className="mt-sp-4 inline-flex min-h-11 items-center rounded-sm bg-[var(--slash)] px-sp-4 font-display text-sm font-semibold normal-case tracking-normal text-[var(--ink)] no-underline transition-colors hover:bg-[var(--white)]"
|
|
onClick={closeMobileMenu}
|
|
>
|
|
Falar sobre meu projeto
|
|
</MagneticButton>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</header>
|
|
);
|
|
}
|