DEV Community

Cover image for Detecting When a Sticky Element Becomes Sticky
Jake Holman
Jake Holman

Posted on • Edited on • Originally published at jakeisonline.com

Detecting When a Sticky Element Becomes Sticky

Ever noticed a sticky header that changes style as you scroll—like the event dates on Luma's site?

Animated GIF showing date headings on Luma event listings becoming sticky and having a hovering badge effect applied

They elegantly transition to a different visual treatment once they become sticky. It’s a small interaction, but it makes the UI feel polished and intentional.

I wanted to figure out how they were doing it.

CSS handles the stickiness (with position: sticky), but there’s no built-in way to detect when it happens.

No CSS selector*. No JavaScript event.

I love figuring out how to replicate cool interactions I see on the web—so here’s how to do this one using the magical IntersectionObserver API.

It’s widely supported, lightweight, and once you know the trick, you’ll start seeing opportunities to use it everywhere.


🧠 Read the full guide with live interactive demos on jakeisonline.com


Have you tried this approach, or come across similar patterns in the wild?

💡 Good news: We’ll soon be able to use container queries to detect when an element becomes sticky.

But as of this article’s publishing, it’s only available in Chrome, so you’re stuck with this for now

Top comments (2)

Collapse
 
dotallio profile image
Dotallio

Really like how you used IntersectionObserver for sticky detection - makes those small transitions feel alive. Have you automated any style changes or effects once the element is sticky?

Collapse
 
jakeisonline profile image
Jake Holman

I have! As much as possible, I like to decouple state-based styling from JavaScript, so the callback in IntersectionObserver is simply changing a data attribute on the sticky element to denote the sticky state.

That way I can rely on CSS to handle the styling changes, in the live example I'm using Tailwind CSS to achieve that:

<div className="sticky detect-sticky top-0 w-fit data-[currently-sticky=true]:border-foreground/10 data-[currently-sticky=true]:drop-shadow-lg data-[currently-sticky=true]:backdrop-blur data-[currently-sticky=true]:bg-background px-4 py-1 border border-transparent rounded-full transition-all duration-150">
  {dayOfMonth} {month} <span className="text-muted-foreground">{day}</span>
</div>
Enter fullscreen mode Exit fullscreen mode

If you're not familiar with Tailwind, the data-[currently-sticky=true] condition placed before any utility class means it's only applied when that condition is true.

Doing this in vanilla CSS would largely look the same, probably using .detect-sticky[data-currently-sticky="true"] as the selector.