Nav Bars
Anchor nav with scroll spy
Section links that follow the reader down the page
Section links that follow the reader down the page
Create a marketing page anchor navigation with Tailwind CSS. The nav is a sticky bar under the header holding five section links in a horizontal row on a white background with a hairline bottom border and, on the right, a small dark "Get started" button that only appears once the page has scrolled past the hero. Links are small and muted, with the active one in neutral-900 carrying a subtle neutral-100 pill background. Below the nav, five demonstration sections with ids, each a tall block with a heading and a paragraph so the behaviour can be seen. Then add JavaScript that observes the sections with IntersectionObserver using a root margin biased toward the top of the viewport, marks the matching link active, reveals the button after 400 pixels of scroll, and scrolls smoothly with an offset that accounts for the sticky bar when a link is clicked. <div class="bg-white">
<section class="mx-auto max-w-3xl px-6 py-24 text-center">
<h1 class="text-5xl font-semibold leading-tight tracking-tight text-neutral-900">A page long enough to need a map</h1>
<p class="mx-auto mt-5 max-w-xl text-lg text-neutral-500">Scroll, and the bar underneath keeps track of where you are.</p>
</section>
<nav id="anchorNav" class="sticky top-0 z-30 border-y border-neutral-200 bg-white/90 backdrop-blur">
<div class="mx-auto flex max-w-4xl items-center justify-between gap-4 px-6 py-2.5">
<div class="flex gap-1 overflow-x-auto">
<a href="#overview" class="anchor-link whitespace-nowrap rounded-lg px-3 py-1.5 text-sm text-neutral-500 transition">Overview</a>
<a href="#pricing" class="anchor-link whitespace-nowrap rounded-lg px-3 py-1.5 text-sm text-neutral-500 transition">Pricing</a>
<a href="#security" class="anchor-link whitespace-nowrap rounded-lg px-3 py-1.5 text-sm text-neutral-500 transition">Security</a>
<a href="#support" class="anchor-link whitespace-nowrap rounded-lg px-3 py-1.5 text-sm text-neutral-500 transition">Support</a>
<a href="#faq" class="anchor-link whitespace-nowrap rounded-lg px-3 py-1.5 text-sm text-neutral-500 transition">FAQ</a>
</div>
<a href="#" id="anchorCta" class="shrink-0 rounded-xl bg-neutral-900 px-4 py-2 text-sm font-medium text-white opacity-0 transition-opacity duration-300">Get started</a>
</div>
</nav>
<main class="mx-auto max-w-3xl px-6">
<section id="overview" class="scroll-mt-24 border-b border-neutral-100 py-24">
<h2 class="text-2xl font-semibold tracking-tight text-neutral-900">Overview</h2>
<p class="mt-3 leading-relaxed text-neutral-500">What the product does, in the two paragraphs someone will actually read before deciding whether to keep scrolling.</p>
</section>
<section id="pricing" class="scroll-mt-24 border-b border-neutral-100 py-24">
<h2 class="text-2xl font-semibold tracking-tight text-neutral-900">Pricing</h2>
<p class="mt-3 leading-relaxed text-neutral-500">Three plans, one of which is free forever, and a sentence explaining what triggers a move between them.</p>
</section>
<section id="security" class="scroll-mt-24 border-b border-neutral-100 py-24">
<h2 class="text-2xl font-semibold tracking-tight text-neutral-900">Security</h2>
<p class="mt-3 leading-relaxed text-neutral-500">Certifications, encryption and the link to the trust centre for the people who will ask for evidence.</p>
</section>
<section id="support" class="scroll-mt-24 border-b border-neutral-100 py-24">
<h2 class="text-2xl font-semibold tracking-tight text-neutral-900">Support</h2>
<p class="mt-3 leading-relaxed text-neutral-500">Who answers, when they answer, and what the median response time actually was last quarter.</p>
</section>
<section id="faq" class="scroll-mt-24 py-24">
<h2 class="text-2xl font-semibold tracking-tight text-neutral-900">FAQ</h2>
<p class="mt-3 leading-relaxed text-neutral-500">The six questions sales gets asked most, answered without hedging.</p>
</section>
</main>
</div> var links = Array.prototype.slice.call(document.querySelectorAll(".anchor-link"));
var cta = document.getElementById("anchorCta");
var sections = links.map(function (link) {
return document.querySelector(link.getAttribute("href"));
});
function activate(id) {
links.forEach(function (link) {
var on = link.getAttribute("href") === "#" + id;
link.classList.toggle("bg-neutral-100", on);
link.classList.toggle("text-neutral-900", on);
link.classList.toggle("font-medium", on);
link.classList.toggle("text-neutral-500", !on);
});
}
// Bias the observation band to the upper third: a heading that has just
// reached the top should win, not whatever happens to fill the viewport.
var observer = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) activate(entry.target.id);
});
},
{ rootMargin: "-20% 0px -70% 0px" }
);
sections.forEach(function (section) {
if (section) observer.observe(section);
});
window.addEventListener(
"scroll",
function () {
cta.classList.toggle("opacity-0", window.scrollY < 400);
},
{ passive: true }
);
activate("overview");