Sticky Page Header Shadow on Scroll

Posted on Apr 2, 2023

Takes about 2 minutes to read

We've seen it plenty of times around the web where a website's page header follows us as we scroll down the page. CSS makes doing this a breeze with sticky positioning:

.page-header {
position: sticky;
top: 0;
}

What if we desired something a little bit extra, like applying a box-shadow to the sticky header as soon as the page is scrolled? I thought it was worth sharing one solution that has worked well for me to accomplish this goal. Check out the following CodePen demo. As soon as the page is scrolled, a shadow fades in below the header.

See the pen (@hexagoncircle) on CodePen.

An element that I've decidedly dubbed an "intercept"—naming is hard and this felt right in the moment—is created and inserted above the page header at the top of the page. If we open the browser dev tools and inspect the DOM, we'll find:

 <div data-observer-intercept></div>
<header id="page-header">
//...
</header>

The Intersection Observer API is being used to observe when the intercept is no longer appearing in the visible viewport area which happens as soon as the page scrolls. So when the intercept is not intersecting, a class is applied to the header element.

const observer = new IntersectionObserver(([entry]) => {
header.classList.toggle("active", !entry.isIntersecting);
});

observer.observe(intercept);

Inspecting the DOM again, we'll catch the active class name on the page header element toggling on and off as we scroll down and back up.

Delay that shadow

It's also possible to wait on when the shadow should appear by offsetting the intercept element. Try editing the above demo on CodePen. In the CSS panel add the following ruleset:

[data-observer-intercept] {
position: absolute;
top: 300px;
}

This will push the intercept down from the top of the page by 300 pixels. When scrolling the page again, notice that the shadow doesn't appear right away, waiting until the page has been scrolled passed the offset value.

Have questions? Other ways to handle this? I'd love to hear about it! Reach out to me on Mastodon with your ideas.

Webmentions for this post

20 likes
3 reposts
20 mentions
  • Andy Bell

    🔗 Good link: ryanmulligan.dev/blog/sticky-he…

  • Yoonsun Lee

    thank you. nice. always trying to solve this.

  • Steve Bauman 💎

    I'd have to see a demo to understand. Can you resolve this issue shown below using your mentioned resolution?

  • Dan Matthews ⚡

    Also should apologise: I thought that was your article @piccalilli_ .

  • Steve Bauman 💎

    Doesn't work when content inside has a background, this was the very first link I tried 😏

  • Andy Bell

    If it's a light background, you can use the multiply blend mode

  • LukyVj - A$AP Luky

    Good share 🫶

  • Dan Matthews ⚡

    I feel like yours and @ste_bau 's last post will compliment each other very nicely.

  • Andy Bell

    Ah yeh, we've been able to do that with CSS for over a decade now! lea.verou.me/2012/04/backgr…

  • Andy Bell

    > I'd rather implement a solution that works for all cases Wouldn't we all, but you certainly won't get that with JavaScript. Anyway, cool article and a nice trick!

  • Steve Bauman 💎

    Ok I'll check it out. Though, I'd rather implement a solution that works for all cases, rather than specific ones.

  • Andy Bell

    well know because that's a red background. Best bet is to look up mix-blend-mode. I wrote a lesson on it in Learn CSS web.dev/learn/css/blen…

  • Pablo Lara H

    👁‍🗨Sticky Page Header Shadow on Scroll by Ryan Mulligan @hexagoncircle @hexagoncircle@fosstodon.org #StickyHeader #css #js #webdev ryanmulligan.dev/blog/sticky-he…

  • Steve Bauman 💎

    Thanks man! 😄

  • narlie choller

    just implemented this! nice one 🧑‍💻

  • David Leininger

    This is smooth: ryanmulligan.dev/blog/sticky-he…. Well done @hexagoncircle

  • Ryan Leichty

    Nice, that’s a clean approach 👏🏻

  • Darryl Yeo 🔜 ETHTokyo 🗼

    What we really need here is a :sticky pseudo-class. That would be a total game changer.

  • Jan Maarten

    Is there a way to make a fancy sticky header that’s accessible? For instance, one where elements in focus never fall “behind” the header?

  • Andy Bell

    not sure tbh