In this tutorial, we’ll learn how to animate header elements on scroll. First, we’ll build a fully responsive top navigation header with three different layouts: one for small screens, one for medium screens, and one for large screens and above.
Next, we’ll smoothly animate its call-to-action button on medium screens and above after a certain amount of scrolling. Sound interesting enough for joining me on the journey?
What We’ll be Building
Here’s a demo video which shows the behavior of our page header:
Here’s the corresponding Codepen demo (check out the larger version to see how the layout changes):
Let’s get started!
1. Begin With the Page Markup
Our page will consist of a header and two helper sections. Inside the header, we’ll place a navigation bar. This will include:
- An image logo.
- The main menu. Its last three items will be visible only on small screens.
- The secondary menu. This will appear on screens greater than 767px. On smaller screens, its items will be part of the main menu.
- A button responsible for toggling the mobile menu. This will be visible on screens up to 1100 pixels.
Here’s the markup:
<header class="page-header"> <nav> <a href=""> <img src="IMG_SRC" alt=""> </a> <ul class="main-menu"> <li> <a href="">Work</a> </li> <li> <a href="">About</a> </li> <li> <a href="">Clients</a> </li> <li> <a href="">News</a> </li> <li> <a href="">Login</a> </li> <li> <a href="">Pricing</a> </li> <li class="btn-wrapper"> <a href="" class="btn">Sign Up</a> </li> </ul> <ul class="secondary-menu"> <li> <a href="">Login</a> </li> <li> <a href="">Pricing</a> </li> <li> <a href="" class="btn">Sign Up</a> </li> </ul> <button class="toggle-mobile-menu" aria-label="Open Mobile Menu" type="button"> <svg width="40" height="40" viewBox="0 0 24 24" fill="none" class="open-menu" aria-hidden="true">...</svg> <svg width="40" height="40" viewBox="0 0 24 24" fill="none" class="close-menu" aria-hidden="true">...</svg> </button> </nav> </header> <section class="hero">...</section> <section class="main-content">...</section>
Beyond the header, we’ll create two sections with dummy content for testing the scrolling effect. For the shake of simplicity, for these elements, we won’t discuss their styles.
Note #1: To avoid creating duplicated content, instead of appending via HTML the last three items of the main menu, we could have dynamically added them via JavaScript. Remember that these are initially part of the secondary menu.
Note #2: For this tutorial, I won’t cover how to make the mobile menu fully accessible. I’ve just used the aria-label
attribute whose value will be updated via JavaScript and the aria-hidden
attribute.
2. Define Some Basic Styles
With the markup ready, we’ll define some basic CSS styles. These will include a Google Font, a few custom variables, and some reset rules:
:root { --white: white; --deeppurple: #7c2a8a; } * { padding: 0; margin: 0; box-sizing: border-box; } ul { list-style: none; } button { background: none; border: none; outline: none; cursor: pointer; } a { text-decoration: none; color: inherit; } ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-track { box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.35); } ::-webkit-scrollbar-thumb { background: var(--deeppurple); } body { font: 20px/1.5 "Inter", sans-serif; }
Within the styles above, just for fun, we added some styles for customizing the default scrollbar styling.
Keep in mind that not all browsers (e.g. Firefox 80) will adopt this new appearance.
Additionally, we’ll specify three helper classes for controlling the visibility of our elements. We’ll use them later when we toggle the mobile menu through JavaScript. Their names are inspired by Bootstrap 4’s class names:
.d-block { display: block !important; } .d-flex { display: flex !important; } .d-none { display: none !important; }
Notice that all include the important
property. As a general rule we should avoid using this property because it messes the styles and makes debugging difficult. In our example though, we’ll use it to apply styles through JavaScript to elements with different levels of specificity.
3. Set the Header Styles
To build the header layout, we’ll follow a desktop-first approach.
Large Screens
On large screens (>1100px), its layout should look like this:
At this point:
- The header will be a fixed positioned element with a static height.
- The navigation will be a flex container. Its contents will be vertically centered across the cross axis and horizontally distributed across the main axis.
- The main menu will also be a flex container with vertically centered items.
- By default, the last item (call-to-action button) of the secondary menu will be off-screen. To push it out of the screen, we’ll give its parent list
transform: translateX(200px)
. The number 200 is derived by adding the button’s width (150px) and the amount of spacing (50px) between the list items of the secondary menu. - The hamburger toggle button will be hidden. This will also contain two icons taken from CSS.gg.
The related styles:
/*CUSTOM VARIABLES HERE*/ .page-header { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; height: 100px; } .page-header nav { position: relative; display: flex; justify-content: space-between; align-items: center; height: 100%; padding: 0 15px; background: var(--white); box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.15); } .page-header ul { display: flex; align-items: center; } .page-header .main-menu { margin-left: 100px; } .page-header .main-menu li:nth-last-child(-n + 3) { display: none; } .page-header .secondary-menu { transform: translateX(200px); transition: transform 0.3s ease-out; } .page-header li:not(:last-child) { margin-right: 50px; } .page-header .btn { display: inline-block; width: 150px; text-align: center; font-weight: 900; padding: 12px 6px; border-radius: 5px; color: var(--white); background: var(--deeppurple); } .page-header a { font-size: 18px; color: var(--deeppurple); } .page-header .toggle-mobile-menu { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .page-header .toggle-mobile-menu .close-menu { display: none; } .page-header .toggle-mobile-menu path { fill: var(--deeppurple); }
Medium Screens
On medium screens (≥768px and ≤1100px), its layout should look like this:
At this point:
- The main menu will be absolutely positioned and shift below the header. It’ll also be hidden by default and appear when we click on the hamburger button. Additionally, its items will be equally distributed across their parent.
- The call-to-action button is still off-screen, yet this time we’ll give its parent
transform: translateX(170px)
because the gap between the list items is decreased to 20 pixels. - The hamburger toggle button will become visible.
The associated styles:
/*CUSTOM VARIABLES HERE*/ @media screen and (max-width: 1100px) { .page-header img { max-width: 140px; } .page-header .main-menu { display: none; position: absolute; top: 100px; left: 0; right: 0; padding: 15px; margin-left: 0; text-align: center; z-index: 1; background: var(--white); box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); } .page-header .main-menu li { flex: 1; } .page-header .secondary-menu { transform: translateX(170px); } .page-header li:not(:last-child) { margin-right: 20px; } .page-header .toggle-mobile-menu { display: block; } }
Small Screens
Finally, on narrow screens (<768px), its layout should look like this:
At this point:
- The main menu items will be stacked. Also, the last three items will become visible.
- The secondary menu will be hidden.
- There won’t be any animation on scroll. The call-to-action will be visible by default, absolutely positioned, and part of the mobile menu.
The corresponding styles:
@media screen and (max-width: 767px) { .page-header { height: 70px; } .page-header .main-menu { top: 70px; flex-direction: column; align-items: start; } .page-header .main-menu li { flex-grow: 0; } .page-header .main-menu li + li { margin-top: 15px; } .page-header .main-menu li:nth-last-child(-n + 3) { display: block; } .page-header .secondary-menu { display: none; } .page-header .toggle-mobile-menu { position: static; transform: none; } .page-header .btn-wrapper { position: absolute; top: 0; right: 15px; } .page-header .btn { width: 120px; padding: 8px 16px; } }
4. Animate on Scroll
As we scroll within the page, we’ll keep track of how much we have scrolled and under a certain amount of scrolling, we‘ll smoothly animate the visibility of the header’s button by toggling the show-btn
class.
In our example, the button will slide-in as soon as the hero section disappears, and vice versa.
In your projects, you can easily change after how much scrolling the button should appear via the targetScroll
variable. Here you can pass either a hardcoded value or a dynamic one.
Here’s the required JavaScript code:
const pageHeader = document.querySelector(".page-header"); const animatedUl = pageHeader.querySelector(".secondary-menu"); const showBtn = "show-btn"; let targetScroll = window.innerHeight - pageHeader.offsetHeight; window.addEventListener("scroll", () => { const scrollY = this.pageYOffset; if (scrollY > targetScroll) { animatedUl.classList.add(showBtn); } else { animatedUl.classList.remove(showBtn); } }); window.addEventListener("resize", () => { targetScroll = window.innerHeight - pageHeader.offsetHeight; });
And the target CSS class:
.page-header .secondary-menu.show-btn { transform: none; }
5. Toggle the Mobile Menu
As the last thing, to make the header fully responsive, let’s create the functionality of the mobile menu.
As soon as we click on the hamburger button, the visibility of the mobile menu will be toggled. At that point, we’ll heavily make use of the helper classes.
Here’s the relevant JavaScript code:
const pageHeader = document.querySelector(".page-header"); const mainMenu = pageHeader.querySelector(".main-menu"); const openMenu = pageHeader.querySelector(".open-menu"); const closeMenu = pageHeader.querySelector(".close-menu"); const toggleMobileMenu = pageHeader.querySelector(".toggle-mobile-menu"); const dNone = "d-none"; const dBlock = "d-block"; const dFlex = "d-flex"; toggleMobileMenu.addEventListener("click", function () { mainMenu.classList.toggle(dFlex); if (!openMenu.classList.contains(dNone)) { this.setAttribute("aria-label", "Close Mobile Menu"); openMenu.classList.remove(dBlock); openMenu.classList.add(dNone); closeMenu.classList.remove(dNone); closeMenu.classList.add(dBlock); } else { this.setAttribute("aria-label", "Open Mobile Menu"); openMenu.classList.remove(dNone); openMenu.classList.add(dBlock); closeMenu.classList.remove(dBlock); closeMenu.classList.add(dNone); } });
You’ve Built a Responsive Header Animation on Scroll!
That’s all, folks! Today we discussed how to create responsive page headers with animated content on scroll. As we saw, with just a few steps we can build this kind of functionality and make eye-catching pages.
Let’s look at our creation once again:
Have you ever built such an animated header for a project in the past? If yes, what awesome techniques have you used?
As always, thanks a lot for reading!
More Tutorials With Animated Headers on Scroll
Learn more about how to animate page headers on scroll with these tutorials:
-
HTMLHow to Hide/Reveal a Sticky Header on Scroll (With JavaScript)
-
PatternsHow to Create a Fixed Header Which Animates on Page Scroll
And if you’re in need of a Flexbox refresher, take a look at these tutorials:
-
FlexboxA Comprehensive Guide to Flexbox Alignment
-
FlexboxA Comprehensive Guide to Flexbox Ordering & Reordering
-
FlexboxA Comprehensive Guide to Flexbox Sizing
No comments:
Post a Comment