{"slug": "sneaky-header-blocker-trick", "title": "Sneaky Header Blocker Trick", "summary": "The article explains a CSS-only technique for creating a sticky header that appears to change background color as a user scrolls down a webpage. The effect is achieved using two separate sticky \"blocker\" elements, each color-matched to their respective sections (blue for the hero and white for the main content), which sit behind the fixed header and seamlessly hand off to one another as the user scrolls past each container. No JavaScript is required for this visual trick, which relies entirely on the behavior of `position: sticky` elements becoming unstuck at the boundaries of their parent containers.", "body_md": "Have you ever noticed that the site header on this blog does something a bit peculiar?\nWhen you first start scrolling, the sticky header appears to have a mostly-opaque blue background, covering up the blog post title/metadata:\nAs you keep scrolling, though, something kinda strange happens. First, the cloud swoops pass by normally, as though the header had a transparent background:\nAnd then, when you get to the content, the header suddenly appears to have a white background:\nThis is a fairly subtle thing, and I suspect most readers have never even noticed it. But there’s something about it which just feels so satisfying to me. It’s one of my favourite little bits of polish on this blog. 😄\nA few times a year, I’ll hear from someone who is completely mystified by the effect and wants to know how I’m doing this. It’s actually quite a bit simpler than most people imagine! There’s no JavaScript involved at all, this is entirely a CSS-based effect.\nIn this blog post, I’m going to reveal the magic trick. We’ll also deepen our understanding of how Positioned layout works in CSS. 🪄\nLink to this headingThe core concept\nHere’s the deal: the site header itself has a transparent background that never changes. It sits in front of the page using position: fixed\nand doesn’t really do anything special.\nThe effect is created by sticky “blocker” elements: one in the hero and one in the main article area. They sit behind the site header, but in front of the other content.\nThis is much easier to explain with a visualization, so I created a minimum viable reproduction of the effect. The “Reveal” slider will highlight what’s actually going on, and you can scroll through this little mockup to see how it works:\n- Categories\n- Courses\n- About\nThe blue hero has a blue rectangle that sits right underneath the real header. It uses position: sticky\n, so it follows the user as they scroll, but critically, it won’t follow the user beyond its container.\nThis is an aspect to sticky positioning that often isn’t super well-understood. When a sticky element reaches the end of its container, it becomes unstuck and scrolls out of view.\nHere’s a playground demonstrating this behaviour. Notice that the orange blocks won’t follow you outside their parent container:\nCode Playground\n<style> .sticky { position: sticky; top: 2px; background: tomato; } </style> <div class=\"container\"> <div class=\"sticky\"></div> </div> <div class=\"container\"> <div class=\"sticky\"></div> </div>\nSo, that’s the core trick! I have two separate “blocker” elements that are each color-matched to their respective backgrounds (a blue blocker in the hero, a white blocker in the main section). As the user scrolls past the hero, we get this neat hand-off; the blue blocker scrolls out of view and the white blocker takes over. ✨\nHere’s a playground showing the code for the minimal reproduction of the effect:\nCode Playground\n<style> html { --blue: hsl(210deg 80% 85%); --header-height: 4rem; } header { position: fixed; z-index: 1; top: 0; height: var(--header-height); } .blocker { position: sticky; top: 0; height: var(--header-height); } .hero .blocker { background: var(--blue); } .main-content .blocker { background: white; } </style> <header>Sticky Header</header> <div class=\"hero\"> <div class=\"blocker\"></div> <h1>Article Title</h1> </div> <main class=\"main-content\"> <div class=\"blocker\"></div> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vel lorem felis. Aliquam finibus libero arcu, ut sodales lorem rutrum vitae. Sed et ornare lorem. Aliquam sagittis neque ac consequat tempus. Phasellus sagittis ipsum ut velit lobortis, a dapibus libero lacinia. Integer accumsan augue sit amet ipsum luctus interdum. Nulla interdum id risus in accumsan. Curabitur quis eleifend orci. </p> <p> Sed sit amet tempus lacus. Aenean faucibus, libero nec posuere maximus, nisi risus laoreet est, ac scelerisque justo quam quis quam. Aliquam imperdiet magna in metus porttitor, vel sollicitudin elit placerat. Integer non elit et ante laoreet blandit vitae non diam. Morbi venenatis nisl nec magna consectetur scelerisque. </p> </main>\nLink to this headingIntegrating swoops\nOne thing that makes this a bit more complicated is the swoops. When I redesigned my blog back in 2024, I went with a cloud motif. The hero includes darker background clouds and a big white puffy foreground cloud:\nFortunately, this doesn’t really affect our main strategy as long as we layer things correctly. From back to front, the layers within the hero are:\n- The text content (blog post title and metadata)\n- The sticky blocker\n- The dark blue background clouds\n- The white foreground cloud\nThe sticky blocker covers the text content, but it slides behind the absolutely-positioned cloud SVGs. The actual site header sits in front of the whole thing.\nLink to this headingDesign requirements\nThere is one significant trade-off with this implementation: we need enough space for the blockers to sit when they’re not actively blocking the content.\nOn this blog, the header is 5rem (typically 80px) tall. That means I need at least 5rem of empty space above the main content, so that the blocker has a place to sit when the user is scrolled all the way to the top of the page:\nThis did actually constrain the design a bit. The cloud swoops couldn’t be as deep or angled as I might’ve preferred.\nOn this blog’s homepage, the clouds are much more aggressive, and there is nowhere for me to hide a blocker element:\nIn theory, I could leave the blocker in this awkward spot and toggle its opacity using JavaScript when the user scrolls past the hero, but in practice, this doesn’t really work; it feels quite jarring if it pops suddenly into existence, but if we give it a short transition, it won’t be fast enough for people who scroll quickly.\nSo, on the homepage, I’m doing things in a more traditional way. The sticky header itself has a background color, and I shift that background from blue to white in JavaScript, based on the scroll position:\nThis effect is nowhere near as satisfying as the sticky blocker trick, but it’s the best solution I’ve found given the design constraints.\nLink to this headingAll of the tricks up my sleeves\nI’ve been building websites for nearly 20 years now, and I’ve picked up so many little techniques like this. If you spend some time poking around my blog, you’ll notice a bunch of little things like this.\nFor the past 18 months, I’ve been working on a new interactive course, Whimsical Animations(opens in new tab). It’s a comprehensive guide that shows you how to create next-level animations and interactions using HTML, CSS, JavaScript, SVG, and Canvas.\nThere are so many golden nuggets in this course. Just about every lesson has some super-practical tip that you can immediately start using in your own work. So many of the techniques I use are not part of the standard web-development skillset. I had to learn all this stuff the hard way, over many many years of experimentation, borrowing techniques from other disciplines like game development and motion design. My goal with this course is to help you skip a few years. ✨\nLearn more here:\nLast updated on\nMay 5th, 2026", "url": "https://wpnews.pro/news/sneaky-header-blocker-trick", "canonical_source": "https://www.joshwcomeau.com/css/header-blockers/", "published_at": "2026-03-23 12:00:00+00:00", "updated_at": "2026-05-22 14:50:31.552862+00:00", "lang": "en", "topics": [], "entities": [], "alternates": {"html": "https://wpnews.pro/news/sneaky-header-blocker-trick", "markdown": "https://wpnews.pro/news/sneaky-header-blocker-trick.md", "text": "https://wpnews.pro/news/sneaky-header-blocker-trick.txt", "jsonld": "https://wpnews.pro/news/sneaky-header-blocker-trick.jsonld"}}