Sticky Header — Free HTML CSS JS Snippet

Sticky Header · Navigation · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

position: fixed with .scrolled class toggled at scrollY > 20
CSS transitions handle all style changes — one class toggle triggers all
backdrop-filter: blur(12px) + rgba(0,0,0,0.9) frosted glass effect
-webkit-backdrop-filter: Safari prefix for cross-browser blur support
Scroll progress bar: scrollY/(scrollHeight-innerHeight) × 100% width
passive: true scroll listener — non-blocking, runs on compositor thread
padding: 16px → 10px transition on scroll for shrink effect
Responsive: nav hides on mobile, hamburger shows, mobile dropdown on toggle

About this UI Snippet

Sticky Header — Scroll-Triggered Blur, Shrink Animation, Progress Bar & Responsive Mobile Nav

Screenshot of the Sticky Header snippet rendered live

A sticky header that changes its appearance on scroll is one of the most universally used navigation patterns on the web. The header starts transparent and full-height at the top of the page, then transitions to a frosted glass appearance with reduced padding as the user scrolls. This snippet implements the complete sticky header pattern: position: fixed, scroll-triggered .scrolled class toggling, backdrop-filter blur, box-shadow transition, padding reduction, a scroll progress bar, and a mobile hamburger navigation.

The scroll event handler

A passive scroll event listener fires on every scroll event. It reads window.scrollY and toggles the .scrolled class on the header when scrollY > 20 (past the initial hero area). The passive: true option tells the browser this listener never calls preventDefault(), allowing it to run on a separate thread without blocking smooth scrolling.

The CSS transition approach

All header style changes are handled by CSS transitions on the .scrolled class — no JavaScript style manipulation. transition: background 0.25s, box-shadow 0.25s, padding 0.25s, backdrop-filter 0.25s on .header animates every property simultaneously when .scrolled is added or removed. The JavaScript only adds or removes one class.

The frosted glass blur

backdrop-filter: blur(12px) creates the frosted glass effect. background: rgba(255,255,255,0.9) provides the semi-transparent tint. -webkit-backdrop-filter is the Safari prefix. The header must NOT be fully opaque — the blurred content behind must show through for the effect to work.

The scroll progress bar

The built-in scroll progress bar uses scrollY / (scrollHeight - innerHeight) × 100 for the scroll percentage. The progress bar uses this percentage as its width. opacity transitions from 0 to 1 when .scrolled is applied, keeping the bar hidden at the top of the page.

Mobile hamburger navigation

On screens under 640px, the nav and CTA buttons hide and the hamburger button appears. Clicking it toggles .mobile-open on the nav (which switches it to an absolute dropdown panel) and .open on the burger (which animates the three spans into an ×). The mobile nav slides in from below the header.

Preventing layout shift

When the header transitions from absolute to fixed (or from tall to short), content can jump because the document flow changes. Prevent this by wrapping the page content in a container with padding-top equal to the header's maximum height. This reserves space for the header at all scroll positions without layout recalculation.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the scroll-to-percentage math by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how maxScroll and pct are derived from scrollHeight and innerHeight, and why passive: true on the scroll listener matters for smooth scrolling on the compositor thread. The same assistant can help optimize it — for instance whether the single scroll handler that both toggles the scrolled class and updates the progress bar width should be split or debounced differently for very long pages, or whether backdrop-filter blur has a measurable cost on lower-end devices. It's also useful for extending the header: ask it to highlight the current nav link based on which section is in view with an IntersectionObserver, add a hide-on-scroll-down/show-on-scroll-up behavior, or make the mobile dropdown animate its height instead of just appearing. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a scroll-reactive "sticky header" in plain HTML, CSS, and JavaScript using a single passive scroll listener and CSS transitions — no framework, no scroll library.

Requirements:
- A fixed-position header that starts transparent with generous padding, and gains a semi-transparent background, backdrop-filter blur, a box-shadow, and reduced padding once the page has scrolled past a small pixel threshold (e.g. 20px) — all of these style changes must be driven by toggling a single CSS class, not by setting individual inline styles from JavaScript.
- A thin scroll progress bar inside the header whose width is set, on every scroll event, to the percentage computed as window.scrollY divided by (document.documentElement.scrollHeight minus window.innerHeight) times 100, clamped to avoid division by zero when the page isn't scrollable.
- The scroll event listener must be registered with the passive: true option so it never blocks the browser's scroll compositor thread.
- A responsive nav that hides the inline links and call-to-action buttons under a set breakpoint and replaces them with a hamburger button; clicking the hamburger must toggle a class that shows an absolutely positioned dropdown panel below the header and simultaneously animates the three hamburger bars into an X shape using CSS transforms, while also updating the button's aria-expanded attribute for accessibility.
- Ensure the header transition covers background, box-shadow, padding, and backdrop-filter together so all these properties change in sync when the scrolled class is toggled.

Step by step

How to Use

  1. 1
    Scroll down to see the header transformPast 20px of scroll the header gains a frosted glass background, box-shadow, and reduced padding. The scroll progress bar appears and grows as you scroll further down the page.
  2. 2
    Resize to mobile to test the hamburger navAt under 640px wide, the nav links and CTA buttons hide and a hamburger button appears. Click it to toggle the mobile dropdown nav. The three lines animate to an × when open.
  3. 3
    Update the logo and nav linksReplace the SVG icon and "Acme" text in .logo. Edit the four .nav-link anchor elements with your page sections and routes. Update .btn-solid and .btn-ghost href values.
  4. 4
    Change the scroll trigger thresholdUpdate scrollY > 20 in the JavaScript to any pixel value. Use > 0 to trigger immediately on any scroll, or > 100 to wait until the user has scrolled past the full hero area.
  5. 5
    Change the frosted glass colourUpdate rgba(255,255,255,0.9) on .header.scrolled for a coloured tint — e.g. rgba(15,23,42,0.8) for a dark header. Also update nav-link, logo, and button colours to maintain contrast on the new background.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component using useEffect for the scroll listener, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

SaaS and product landing page navigation
The frosted glass sticky header is the standard navigation pattern for modern SaaS landing pages. It keeps the nav accessible throughout a long scroll while the blurred glass gives it a premium look without obscuring page content.
Blog and content site reading progress indicator
The scroll progress bar is particularly valuable on long-form content pages — articles, documentation, tutorials. It communicates reading progress at a glance without requiring the user to estimate their position in the document.
Marketing and campaign landing pages
Marketing pages need headers that transition from hero overlay to sticky nav as users read down. The transparent-to-frosted-glass transition avoids covering hero imagery while providing a readable nav during scroll.
Responsive navigation with mobile hamburger menu
The snippet includes a complete mobile nav: hamburger button that animates to ×, dropdown panel, and proper aria-expanded attributes. Test the full mobile experience by resizing the preview to under 640px.
Study the CSS class-toggle approach to scroll animations
This snippet demonstrates the cleanest approach to scroll-triggered animations: JavaScript adds/removes one class, CSS transitions handle everything else. No inline style changes, no JavaScript animation — all visual logic lives in CSS.
E-commerce and documentation site persistent nav
Documentation sites and e-commerce sites need headers visible at every scroll position. The sticky header with shrink effect is space-efficient — it takes less vertical space after scroll, giving more room to the content below.

Got questions?

Frequently Asked Questions

The passive: true option tells the browser that this scroll listener will never call e.preventDefault(). This allows the browser to continue smooth scrolling on a separate thread (the compositor) without waiting for the JavaScript to finish. Without passive: true, the browser must wait for the scroll handler to complete before advancing the scroll position, which can cause jank on slow devices. Always add passive: true to scroll, touchstart, and touchmove listeners that do not prevent default.

For a dark hero, start the header with no background (remove the default white background). Set nav-link, logo, and button text colours to white or light grey. When .scrolled applies, the dark rgba background and light text remain. Add a CSS transition for color on .nav-link and .logo so the text colour also transitions smoothly when the header changes state: .header.scrolled .nav-link { color: #64748b; }.

For a multi-page site, add .active to the link matching the current URL: const current = window.location.pathname; document.querySelectorAll(".nav-link").forEach(link => { link.classList.toggle("active", link.getAttribute("href") === current); }). For smooth scroll single-page navigation, use IntersectionObserver to add .active to the link corresponding to the currently visible section. Style .active: color: #6366f1; font-weight: 600.

Click "JSX" to download. The header component needs "use client" since it uses a scroll event listener. In a useEffect: window.addEventListener("scroll", handler, { passive: true }); return () => window.removeEventListener("scroll", handler). Manage scrolled state with useState(false). In Next.js, use this as a client component inside your app/layout.tsx server component: import Header from "@/components/Header"; export default function Layout({ children }) { return <><Header />{children}</>; }.