diff --git a/apps/web/src/components/faq/FaqSection.tsx b/apps/web/src/components/faq/FaqSection.tsx index f46b3931..63231f04 100644 --- a/apps/web/src/components/faq/FaqSection.tsx +++ b/apps/web/src/components/faq/FaqSection.tsx @@ -38,24 +38,26 @@ export function FaqSection() { className="w-[30px] lg:w-[50px] absolute right-0 top-0" /> +
- - {faqs.map((faq, index) => ( - - - {faq.question} - - - {faq.answer} - - - ))} - -
+ + {faqs.map((faq, index) => ( + + + {faq.question} + + + + {faq.answer} + + + ))} + + ); diff --git a/apps/web/src/components/landing-sections/navbar.tsx b/apps/web/src/components/landing-sections/navbar.tsx index 580b943d..8f356303 100644 --- a/apps/web/src/components/landing-sections/navbar.tsx +++ b/apps/web/src/components/landing-sections/navbar.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import PrimaryButton from "../ui/custom-button"; import { motion, useScroll, useMotionValueEvent } from "framer-motion"; import Image from "next/image"; @@ -15,6 +15,7 @@ const Navbar = () => { const isPricingPage = pathname === "/pricing"; const [showNavbar, setShowNavbar] = useState(isPricingPage ? true : false); const [isOpen, setIsOpen] = useState(false); + const [activeSection, setActiveSection] = useState(""); const { trackButtonClick, trackLinkClick } = useAnalytics(); const handleGetStartedClick = (location: "navbar" | "mobile_menu") => { @@ -30,18 +31,19 @@ const Navbar = () => { ); }; - React.useEffect(() => { + // Close mobile menu on Escape + useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape" && isOpen) { setIsOpen(false); (document.activeElement as HTMLElement)?.blur(); } }; - document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); }, [isOpen]); + // Show navbar when scrolling down (except on Pricing page) useMotionValueEvent(scrollYProgress, "change", (latest) => { if (!isPricingPage) { setShowNavbar(latest > 0); @@ -53,23 +55,67 @@ const Navbar = () => { { name: "Features", href: "/#features" }, { name: "Demo", href: "/#demo" }, { name: "How it works", href: "/#HIW" }, + { name: "FAQ", href: "/#faq" }, { name: "Stats", href: "/#Stats" }, { name: "Contact", href: "/#Contact" }, - { name: "FAQ", href: "/#faq" }, + ]; + +// scroll spy to highlight active section +useEffect(() => { + const sectionIds = links + .filter((link) => link.href.startsWith("/#")) + .map((link) => link.href.replace("/#", "")); + + const handleIntersect = (entries: IntersectionObserverEntry[]) => { + // Filter for intersecting sections + const visibleEntries = entries.filter(entry => entry.isIntersecting); + + if (visibleEntries.length > 0) { + // Find the section with highest visibility + let mostVisibleEntry = visibleEntries[0]; + + for (const entry of visibleEntries) { + if (entry.intersectionRatio > mostVisibleEntry.intersectionRatio) { + mostVisibleEntry = entry; + } + } + + // Update active section if target has an id + if (mostVisibleEntry.target.id) { + setActiveSection(mostVisibleEntry.target.id); + } + } + }; + + const observer = new IntersectionObserver(handleIntersect, { + root: null, + rootMargin: "0px", + threshold: 0.5, + }); + + sectionIds.forEach((id) => { + const el = document.getElementById(id); + if (el) observer.observe(el); + }); + + return () => observer.disconnect(); +}, [links]); + return ( + {/* Left: Logo + Menu */}
+ + {/* Center: Desktop Links */}
{links.map((link, index) => { - const isActive = pathname === link.href; + const isActive = + pathname === link.href || + (link.href.startsWith("/#") && + activeSection === link.href.replace("/#", "")); + return ( {link.name} + + {isActive && ( + +)} ); })}
+ + {/* Right: Buttons */}
{
+ + {/* Mobile Menu */} {isOpen && ( { ); }; -export default Navbar; +export default Navbar; \ No newline at end of file