82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* SiteHeader — fixed top nav. Compacts (adds a glass backdrop) once you scroll
|
|
* past the hero, and includes a mobile menu toggle. The wordmark is real text
|
|
* ("feedback studios") with the brand mark inline.
|
|
*/
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import PillMark from "./PillMark";
|
|
|
|
const links = [
|
|
{ href: "#services", label: "Services" },
|
|
{ href: "#work", label: "Work" },
|
|
{ href: "#process", label: "About" },
|
|
{ href: "#faq", label: "FAQ" },
|
|
];
|
|
|
|
export default function SiteHeader() {
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [open, setOpen] = useState(false);
|
|
const headerRef = useRef<HTMLElement>(null);
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 80);
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, []);
|
|
|
|
// Close the mobile menu on Escape.
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") setOpen(false);
|
|
};
|
|
document.addEventListener("keydown", onKey);
|
|
return () => document.removeEventListener("keydown", onKey);
|
|
}, [open]);
|
|
|
|
return (
|
|
<header
|
|
ref={headerRef}
|
|
className={`site-header${scrolled ? " is-scrolled" : ""}${open ? " is-open" : ""}`}
|
|
>
|
|
<div className="site-header__inner">
|
|
<a className="brand hoverable" href="#top" aria-label="Feedback Studios, home">
|
|
<PillMark className="brand__mark" title="Feedback Studios" />
|
|
<span className="brand__word">
|
|
feedback<span className="brand__word-2">studios</span>
|
|
</span>
|
|
</a>
|
|
|
|
<nav className="site-nav" aria-label="Primary">
|
|
<ul>
|
|
{links.map((l) => (
|
|
<li key={l.href}>
|
|
<a className="hoverable" href={l.href} onClick={() => setOpen(false)}>
|
|
{l.label}
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<a className="btn btn--sm btn--primary hoverable" href="#contact" onClick={() => setOpen(false)}>
|
|
<span>Get a growth audit</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<button
|
|
type="button"
|
|
className="site-header__toggle hoverable"
|
|
aria-expanded={open}
|
|
aria-label={open ? "Close menu" : "Open menu"}
|
|
onClick={() => setOpen((v) => !v)}
|
|
>
|
|
<span />
|
|
<span />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|