In a previous tutorial, we learned how to hide and reveal a sticky header on scroll. Today, let's build something similar: a sticky header whose top part (the notification header bar) will disappear on scroll down and appear on scroll up.
What We’re Building
Here’s what we're going to create (scroll to test the behavior):
Let’s get started!
1. Begin With the Page Markup
The header will consist of two parts: the top and bottom.
- The top part will contain a special notification about free shipping and the contact info.
- The bottom part will contain the company logo and menu. By default, the menu won’t appear.
This header structure is common, especially on eCommerce stores. The header/notification bar is used to notify users about special discounts, promotions, delivery info, and other things. They play a very important role as they are the very first thing a visitor sees on the website. A smart notification bar can attract more shoppers and boost sales.
Beyond the header, just to create enough scrolling for testing the effect, we’ll also specify some dummy sections.
Here’s the header markup:
<header class="page-header"> <nav> <div class="header-top"> <div class="container"> <div class="d-flex flex-column flex-row@s justify-content-between text-center"> <div>...</div> <div>...</div> </div> </div> </div><!-- /header-top --> <div class="header-bottom"> <div class="container"> <div class="d-flex justify-content-between"> <a href=""> <img width="178" height="38" src="horizontal-logo-mobile.svg" alt="forecastr logo"> </a> <div class="d-flex"> <ul class="menu d-flex flex-column flex-row@m align-items-center justify-content-between">...</ul> <button aria-label="Toggle menu" class="toggle-menu"> <svg aria-hidden="true" width="30" height="30" viewBox="0 0 24 24"> <path d="M4 22h-4v-4h4v4zm0-12h-4v4h4v-4zm0-8h-4v4h4v-4zm3 0v4h17v-4h-17zm0 12h17v-4h-17v4zm0 8h17v-4h-17v4z" /> </svg> </button> </div> </div> </div> </div><!-- /header-bottom --> </nav> </header> <!-- sections here -->
2. Define Some Helper Classes
For the sake of simplicity, we’ll only discuss the styles related to the header. You can examine all the project styles by clicking the CSS tab of the demo project.
To style the header, we’re going to need some helper classes. Rather than including a CSS framework like Tailwind, let’s define these classes by ourselves.
.container { padding: 0 15px; max-width: 100%; } .d-flex { display: flex; } .flex-column { flex-direction: column; } .align-items-center { align-items: center; } .justify-content-between { justify-content: space-between; } .text-center { text-align: center; } @media (min-width: 600px) { .container { padding: 0 30px; } .flex-row\@s { flex-direction: row; } } @media (min-width: 900px) { .flex-row\@m { flex-direction: row; } }
3. Specify the Header Styles
On small screens (<600px), the header layout will look like this:
On screens between 600px and 899px, its layout will appear as follows:
Finally, on larger screens (≥900px), it will have the following appearance:
Let’s take note of the main styles:
- Both the top and bottom parts will be fixed positioned elements and for flexibility reasons, they won’t have a static height.
- The bottom part will be positioned right underneath the top part. As we’ll see in a bit, we’ll use JavaScript to calculate its
top
property value. - As we’ve already said, by default, the menu will be invisible. On screens up to 899px, it will be positioned absolutely, while on larger screens, it will be part of the normal document flow.
Here are the associated styles:
/*CUSTOM VARIABLES HERE*/ .page-header .header-top, .page-header .header-bottom { position: fixed; left: 0; right: 0; z-index: 1; transition: all 0.4s; } .page-header .header-top { top: 0; padding: 10px 0; background: var(--pink); } .page-header .header-bottom { padding: 20px 0; background: var(--white); box-shadow: 0 -2px 10px rgb(0 0 0 / 15%), 0 5px 5px rgba(0, 0, 0, 0.15); } .page-header .header-bottom .menu { position: absolute; top: 100%; left: 0; right: 0; opacity: 0; visibility: hidden; padding: 15px; border-top: 1px solid whitesmoke; transform: translateY(20px); box-shadow: 0 5px 5px rgba(0, 0, 0, 0.15); background: var(--white); transition: all 0.2s; } .page-header .header-bottom .menu li:not(:last-child) { margin-bottom: 15px; } @media (min-width: 900px) { .page-header .header-bottom .menu { position: static; padding: 0; border: none; margin-right: 80px; background: none; box-shadow: none; } .page-header .header-bottom .menu li:not(:last-child) { margin: 0 30px 0 0; } }
4. Add the JavaScript
For the next step let’s add some behavior to the header.
Calculate the Offsets
When the DOM is ready and when we resize the browser window the calculateOffsets()
function will fire.
This function will be responsible for setting the top
property value of the bottom header part and the padding-top
property value of the body
. These actions are necessary as both header parts are fixed elements, so they are completely removed from the normal document flow.
Here’s the required JavaScript code:
calculateOffsets(); window.addEventListener("resize", () => { calculateOffsets(); }); function calculateOffsets() { const headerTopHeight = headerTop.offsetHeight; const headerBottomHeight = headerBottom.offsetHeight; headerBottom.style.top = `${headerTopHeight}px`; body.style.paddingTop = `${headerTopHeight + headerBottomHeight}px`; }
Toggle Menu
Each time we click on the toggle button, the menu’s visibility will change. If it’s hidden, it will appear, and vice versa.
Here’s the related JavaScript code:
const menu = document.querySelector(".page-header .header-bottom .menu"); const toggleMenu = document.querySelector( ".page-header .header-bottom .toggle-menu" ); toggleMenu.addEventListener("click", () => { menu.classList.toggle("is-visible"); });
And the styles:
.page-header .header-bottom .menu { transition: all 0.2s; } .page-header .header-bottom .menu.is-visible { opacity: 1; visibility: visible; transform: none; }
Toggle Header Bar
Now for the primary target of this tutorial.
Each time we scroll down, the header bar should smoothly disappear and come back into view as we scroll back up.
To implement this we’ll follow the same pattern as we did in the previous tutorial, thus we’ll borrow a decent amount of code from it.
Regarding the functionality:
- As we scroll down, the
body
will receive thescroll-down
class. At this point, the top bar will be animated to the top and become hidden. At the same time, the bottom part will be animated upwards and occupy the empty space, plus we’ll recalculate thepadding-top
property value of thebody
. - As we scroll up, it will receive the
scroll-up
class. Note that we won’t need this class, yet it will be at our disposal if we want to modify the demo styles through CSS later. At this point, we’ll remove the CSS transforms from the header parts and again recalculate thepadding-top
property value of thebody
. - If we scroll to the top of the page, it will lose its
scroll-up
class.
Here’s the (quite extensive!) JavaScript code to handle all this stuff:
const body = document.body; const headerTop = document.querySelector(".page-header .header-top"); const headerBottom = document.querySelector(".page-header .header-bottom"); const menu = document.querySelector(".page-header .header-bottom .menu"); const toggleMenu = document.querySelector( ".page-header .header-bottom .toggle-menu" ); const scrollUp = "scroll-up"; const scrollDown = "scroll-down"; let lastScroll = 0; window.addEventListener("scroll", () => { const currentScroll = window.pageYOffset; const headerTopHeight = headerTop.offsetHeight; const headerBottomHeight = headerBottom.offsetHeight; if (currentScroll <= 0) { body.classList.remove(scrollUp); return; } if (currentScroll > lastScroll && !body.classList.contains(scrollDown)) { // down body.classList.remove(scrollUp); body.classList.add(scrollDown); headerBottom.style.transform = `translateY(-${headerTopHeight}px)`; body.style.paddingTop = `${headerBottomHeight}px`; } else if ( currentScroll < lastScroll && body.classList.contains(scrollDown) ) { // up body.classList.remove(scrollDown); body.classList.add(scrollUp); headerBottom.style.transform = "none"; body.style.paddingTop = `${headerTopHeight + headerBottomHeight}px`; } lastScroll = currentScroll; });
And the associated styles:
/*CUSTOM VARIABLES HERE*/ .page-header .header-top, .page-header .header-bottom { transition: all 0.4s; } .scroll-down .header-top { transform: translate3d(0, -100%, 0); }
Conclusion
That’s it, folks! During this exercise, we learned how to toggle a notification bar depending on the scrolling direction. You can utilize this effect primarily in eCommerce stores to create smart header bars that will increase your sales and make conversions.
Let’s look again at what we built today:
As always, thanks a lot for reading!
More Sticky Tutorials
Stick around! There’s plenty more to learn about sticky behavior in web design:
No comments:
Post a Comment