feat(Navbar): active section highlight via IntersectionObserver

This commit is contained in:
Marco
2026-06-30 22:19:57 +00:00
parent 023367436c
commit 38da6b503e
+37 -1
View File
@@ -33,8 +33,13 @@ const navLinks = [
{ label: "Shop", href: "/shop" },
];
const anchorIds = navLinks
.filter((l) => l.href.startsWith("#"))
.map((l) => l.href.slice(1));
export function Navbar() {
const [scrolled, setScrolled] = useState(false);
const [activeSection, setActiveSection] = useState("");
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 8);
@@ -42,6 +47,33 @@ export function Navbar() {
return () => window.removeEventListener("scroll", onScroll);
}, []);
useEffect(() => {
const visible = new Set<string>();
const updateActive = () => {
for (const id of anchorIds) {
if (visible.has(id)) { setActiveSection(id); return; }
}
setActiveSection("");
};
const observers = anchorIds.map((id) => {
const el = document.getElementById(id);
if (!el) return null;
const obs = new IntersectionObserver(
([entry]) => {
entry.isIntersecting ? visible.add(id) : visible.delete(id);
updateActive();
},
{ rootMargin: "-100px 0px -50% 0px" }
);
obs.observe(el);
return obs;
});
return () => observers.forEach((o) => o?.disconnect());
}, []);
return (
<header
className={`sticky top-0 z-50 w-full h-[6.25rem] flex items-center px-[2rem] transition-[background-color,backdrop-filter] duration-300 ${
@@ -87,7 +119,11 @@ export function Navbar() {
}
history.replaceState(null, "", link.href);
}}
className="text-[1.125rem] font-semibold text-[#222221] tracking-[0.01125rem] hover:text-[#f6a701] transition-colors cursor-pointer"
className={`text-[1.125rem] font-semibold tracking-[0.01125rem] transition-colors cursor-pointer ${
activeSection === link.href.slice(1)
? "text-[#f6a701]"
: "text-[#222221] hover:text-[#f6a701]"
}`}
>
{link.label}
</a>