Source Code

<div class="page-mock">
  <header class="site-header">Store Header</header>
  <div class="filter-bar" id="filterBar">
    <div class="chips">
      <button class="chip active" onclick="selectChip(this)">All</button>
      <button class="chip" onclick="selectChip(this)">Clothing</button>
      <button class="chip" onclick="selectChip(this)">Shoes</button>
      <button class="chip" onclick="selectChip(this)">Accessories</button>
    </div>
    <select class="sort-select" aria-label="Sort products">
      <option>Sort: Featured</option>
      <option>Price: Low to High</option>
      <option>Price: High to Low</option>
      <option>Newest</option>
    </select>
  </div>
  <div class="scroll-content" id="scrollContent">
    <div class="fake-card"></div>
    <div class="fake-card"></div>
    <div class="fake-card"></div>
    <div class="fake-card"></div>
    <div class="fake-card"></div>
    <div class="fake-card"></div>
  </div>
</div>

Sticky Filter Bar — Free HTML CSS JS Sticky Category & Sort Bar Snippet

Sticky Filter Bar · Scroll · Plain HTML, CSS & JS · Live preview

What's included

Features

Pure CSS position: sticky pinning — no scroll-offset math required for the core behavior
JavaScript only adds a cosmetic drop shadow once the bar is actually pinned
backdrop-filter blur keeps scrolling content from showing through the semi-transparent bar
Single-select filter chips with the standard active-class toggle pattern
Native <select> for sort options, preserving built-in keyboard and mobile UX for free
Works correctly with any fixed site header by adjusting the sticky top offset
Flex-wrap layout keeps chips and sort control usable on narrow viewports
Zero external dependencies — pure CSS sticky plus about five lines of JS

About this UI Snippet

Sticky Filter Bar — HTML, CSS & JavaScript Sticky Category Bar

Screenshot of the Sticky Filter Bar snippet rendered live

On category and search-results pages, users often scroll well past the top before deciding they want to change a filter or the sort order. Making them scroll back up every time is friction worth removing — this snippet keeps the filter chips and sort dropdown pinned to the top of the viewport as soon as the page scrolls past them, using CSS position: sticky rather than a JavaScript-computed position: fixed.

Why position: sticky instead of position: fixed

A position: fixed element is removed from document flow entirely and needs manual JavaScript to calculate exactly when it should start behaving like it's fixed (typically by comparing scroll offset against the element's original position, and toggling a class). position: sticky with top: 0 does that calculation natively in the browser's layout engine: the element scrolls normally with the page until it reaches the specified top offset, then "sticks" there for as long as its parent container is still in view. No scroll-offset math, no layout thrashing, no janky recalculation on resize.

Why JavaScript is still used here

CSS sticky positioning handles *where* the bar sits, but it doesn't natively expose "is the element currently stuck" as a style hook. To add a drop shadow only once the bar is pinned (a common visual affordance telling users "this is now floating above your content"), a small scroll listener checks scrollTop > 4 and toggles a .stuck class that adds a box-shadow. This is a purely cosmetic enhancement — the sticky positioning itself works with zero JavaScript.

How the filter chips work

Each chip is a plain button; selectChip(btn) removes .active from every chip and adds it to the clicked one — the same single-selection pattern used throughout this snippet library. The sort control is left as a native <select> for built-in keyboard and mobile accessibility, though it could be swapped for the custom sort-dropdown pattern from the companion "Sort Dropdown" snippet.

Stacking context note

The filter bar needs z-index higher than the content beneath it so it visually sits above scrolling cards once stuck, and its background needs to be opaque (or a translucent blur, as used here with backdrop-filter) so content doesn't show through the bar while it's pinned.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant to explain the specific difference in browser behavior between position: sticky and a JavaScript-driven position: fixed toggle, including what "nearest scrolling ancestor" means for sticky and why that matters if this bar is nested inside another scrollable container. It's also worth asking the assistant to add a subtle slide-down entrance animation the first time the bar becomes stuck, or to combine this pattern with the custom sort-dropdown snippet for a fully custom-styled sticky toolbar.

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 "sticky filter bar" in plain HTML, CSS, and minimal vanilla JavaScript for an e-commerce or search-results page.

Requirements:
- A horizontal bar containing a row of category filter chip buttons (one marked active at a time via a single JS function) and a sort control, positioned directly below a page header in normal document flow.
- The bar must use CSS position: sticky with a top offset so that once the user scrolls past its original position, it pins to the top of the scrolling container instead of scrolling away — do not calculate this with JavaScript scroll-offset math or position: fixed.
- Add a small scroll listener purely for a cosmetic enhancement: toggle a class that adds a drop shadow to the bar once it is actually in its stuck/pinned state, so users get a visual cue it is now floating above the content.
- The bar's background must be semi-transparent with a backdrop blur so that content scrolling underneath it doesn't create harsh overlaps with the bar's own text.
- The layout must wrap gracefully on narrow viewports (chips wrapping to a second line rather than overflowing) and must work correctly when nested inside a scrollable container, not just the top-level page.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the snippetClick "Sticky Filter Bar" in the sidebar Library tab to load the scrollable page mock.
  2. 2
    Scroll the previewScroll down inside the preview panel and watch the filter bar pin to the top and gain a shadow.
  3. 3
    Try the chips and sortClick different category chips and open the sort dropdown to see the interactive controls.
  4. 4
    Adjust the stick thresholdChange the top: 0 value in .filter-bar to stick a few pixels lower, e.g. below a fixed site header.
  5. 5
    Swap the native selectReplace <select> with the custom "Sort Dropdown" snippet if you want fully custom-styled sort options.
  6. 6
    Export and saveExport as HTML/JSX/Tailwind or click "Save as" to reuse this bar on a real category page.

Real-world uses

Common Use Cases

SHOP
E-commerce category pages
Keep category filters and sort controls reachable no matter how far a shopper scrolls down a long product grid.
Search results pages
Pin filter and sort controls above scrolling search results so refining a query never requires scrolling back up.
Data table toolbars
Apply the same sticky pattern to a table's filter/search toolbar so it stays accessible while scrolling long tables.
Learn position:sticky mechanics
Study how sticky positioning differs from fixed positioning and why it needs no manual scroll-offset calculation.
Content-heavy blog or article filters
Adapt the chip row to filter blog posts by tag while keeping the filter bar reachable during a long scroll.

Got questions?

Frequently Asked Questions

position: sticky lets the browser handle the transition between normal document flow and a pinned state natively, based on the top offset and the parent container's bounds. position: fixed requires manually calculating scroll offsets in JavaScript and toggling classes, which is more code and more prone to jank.

The JS only adds a drop shadow once the bar is actually stuck, as a visual cue that it is now floating above content. The positioning behavior itself requires no JavaScript at all — the shadow is a purely cosmetic enhancement.

Set the .filter-bar top value to the header's height (e.g. top: 64px if the header is 64px tall) instead of top: 0, so the bar sticks directly beneath the header rather than underneath the viewport edge.

Once the bar is pinned, content scrolls directly underneath it. A translucent, blurred background keeps text from directly overlapping the bar's own text while still feeling connected to the page, rather than looking like a harsh opaque box.

Yes — sticky positioning works relative to the nearest scrolling ancestor, which is why this demo works correctly inside the scrollable .page-mock container used for the preview.

Yes — swap it for a custom-styled dropdown menu (see the companion "Sort Dropdown" snippet) if you want full control over the option list's appearance, at the cost of needing to reimplement keyboard navigation yourself.

position: sticky has broad modern browser support (all evergreen browsers). If you need to support very old browsers without sticky support, the bar will simply scroll away normally as a fallback, with no errors.