How to Create a Nav Bar on Scroll

Muhammad Saqib Ilyas
9 min readJul 28, 2024

--

A nav bar on scroll is a navigation bar that is hidden initially, but appears as soon as the user scrolls. Here’s an animation that shows one in action. We’ll learn how to build it in this blog, step by step.

An animated preview of what we’ll build

The initial HTML

Let’s start with the following HTML in a file named index.html:

<!DOCTYPE html>
<html>
<head>
<title>Sticky navigation bar on scroll</title>
<link rel="stylesheet" href="style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;500;600;700;800&family=Roboto:wght@300&display=swap" rel="stylesheet">
</head>
<body>
<header>
<div class="logo">ECO LIVE</div>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#products">Products</a></li>
<li><a href="#about">About</a></li>
<li><a href="#gallery">Gallery</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
<section class="content">
<p>Global warming is a real phenomenon. It is a threat to our very existence. It is estimated that
by the year 2050, we will be unable to sustain. With unchecked industrialization and commercialization,
we have caused serious harm to plant and animal life on earth.</p>
<p>The global eco system has been badly damaged.
Many creatures are near extinction. The sea temperatures are rising, causing serious threat to the
ocean life. The glaciers are melting at an alarming pace. Yet, we are not acting.
</p>
<p>We are a global consortium of organizations, and individuals campaigning for climate action. We have
started various practical intiatives. This includes tree plantation campaigns, creating animal habitats,
protecting river life systems, and much more. Our various programs have created real measurable impact.
</p>
</section>
<script src="app.js"></script>
</body>
</html>

In the head section, we link to a style.css file. This file should be placed in the same directory as the index.html file. We import the Poppins and Roboto fonts from Google fonts to create a non-boring look. In the body section, we start with a header section to house the logo and the navigation bar. We create the logo out of text inside a div element. We create the navigation bar using a nav element with an unordered list inside it. We create a section element to house some text in p elements. Finally, we include a JavaScript file at the end of the body element. At this point, the page looks really plain and nothing like we intend it to be. But, slowly and gradually, we’ll get there.

The initial CSS

Create a style.css file in the same directory as index.html. Let’s start with a CSS reset to get rid of browser-specific default stylesheet differences.

*, ::after, ::before {
margin: 0;
padding: 0;
box-sizing: border-box;
}

Next, let’s select the font for the document.

html {
font-family: 'Poppins', 'Open Sans', sans-serif;
font-size: 62.5%;
}

We select the Poppins font to be used by default. If that font isn’t available on the device, we specify fallbacks of Open Sans, and sans-serif. We set the font-size to be 62.5%. What does that achieve? The default font size in most web browsers is 16px, 62.5% of which is 10px. That sets our base font size at 10px. Now, when we use rem units, we have convenient calculations. That is, 1rem is 10px, 2rem is 20px etc. This makes it convenient to size the elements on the page.

Next, let’s style the logo.

.logo {
font-weight: 400;
font-size: 3rem;
margin-left: 1rem;
}

We select a font size of 400 to make it look bold. We set the font size to 30px. We give a 10 pixels margin on the left so that it is a bit away from the edge of the page.

Making the page scroll

We can’t have nav bar on scroll, if there’s no scroll to begin with. We’ll accomplish that by letting the top element on the page occupy all of the screen height. We’ll fill it with a background image. I downloaded a free one on the theme of global warming.

header {
width: 100%;
height: 100vh;
background: url('globalwarming.jpeg') no-repeat 50% 50%;
background-size: cover;
background-attachment: fixed;
}

We select the header element using an element selector. We set it to occupy 100% of the page width. We set it to occupy the entire page height. We set an image as the background. We use no-repeat so that the image isn’t repeated in case the screen dimensions are bigger than the background image. With 50% 50%, we align the image’s center with the center of the container element. By setting background-size to cover, we have the background image stretch to fill the container element. Finally, thanks to background-attachment set to fixed, the background stays in place when the user scrolls the page.

Here’s what the top of the page looks like at the moment:

Our page after initial styling

While we’re here, let’s style the textual content portion as well.

.content {
width: 100%;
margin: 2rem auto;
font-size: 2rem;
line-height: 1.5;
text-align: justify;
}

We use the class selector .content. We let the text occupy all of the page width. We give it a bit of margin above and below. We select a font size of 20 pixels. We set the line height to 1.5 so that the lines of text are comfortably spaced away from each other. We set the alignment of text to justify.

Styling the navigation bar

The navigation menu needs to be horizontal on the page and next to the logo, not below it. Also, the logo and the navigation menu need to stay at the same position even when the page scrolls. To achieve that, we add position: fixed; to the logo class and the nav element.

.logo {
position: fixed;
font-weight: 400;
font-size: 3rem;
margin-left: 1rem;
}

header nav {
position: fixed;
width: 100%;
}

At the moment, the menu is still vertical, and it is above the logo. To fix the latter, we set the z-index of the logo to 2 and that of the menu to 1.

.logo {
position: fixed;
font-weight: 400;
font-size: 3rem;
margin-left: 1rem;
z-index: 2;
}

header nav {
position: fixed;
width: 100%;
z-index: 1;
}

To fix the former, we can use the following:

header nav ul {
list-style: none;
text-align: center;
}

header nav ul li {
display: inline-block;
padding: 2rem;
}

We remove the list styling from the ul element. Since the nav element has a width of 100%, setting text-align to centerbrings the menu to the center of the page. We change the display property of the li elements to inline-block so that the successive list items don’t start on the separate lines. This makes the menu horizontal. We apply a 20 pixel padding all around the list items for a better layout.

Let’s improve the appearance of the menu items.

header nav ul li a {
text-decoration: none;
color: rgba(0, 0, 0, 0.85);
font-size: 1.3rem;
font-family: 'Roboto';
text-transform: uppercase;
letter-spacing: 2px;
}

We select the a elements and get rid of the default decorations applied by the browsers so that they look less like hyperlinks, and more like a menu. We give these elements a dark gray color. We set a font size of 13 pixels with the Roboto font. We apply a text transform of uppercase so that the menu text appears in all caps. We apply a 2 pixel letter spacing so that the menu items appear better.

After the above changes, the menu looks like the following.

Our menu after the styling

On scroll behavior

We want the navigation bar to become more prominent when the user scrolls. For that, let’s prepare a style specification, first.

header nav.white ul {
background: #FFF;
}

Simple enough! To contrast with the background image, we set a white background. This style applies to the ul elements that are descendants of a nav element with a class of white, that is a descendant of the header element. Now, all we need to do is to apply the class named white to the nav element when the user scrolls beyond a certain point. That brings in JavaScript.

Create a file named app.js in the same directory as the index.html file. Recall that we had already referred to this file in the index.html file. Put the following code in it.

const nav = document.querySelector('header nav')
window.onscroll = () => {
if (window.scrollY > 0) {
nav.classList.add('white')
}
else {
nav.classList.remove('white')
}
}

We use the querySelector() method to select the nav element and save the object in a variable named nav. We set an anonymous function as the scroll event handler of the window object. In that function, we check the scrollY property of the window object. This property indicates the number of pixels by which the user has vertically scrolled on the page. A value of zero indicates that the window isn’t scrolled. So, if this value is greater than zero, we add the class of white to the nav element, otherwise, we remove it. Now, as soon as you scroll a little, you should see the navigation menu become prominent as shown below.

The navigation menu on scroll

The appearance of the new style is very sudden. Let’s apply a bit of a transition delay to it.

header nav ul {
list-style: none;
text-align: center;
transition: 0.8s ease;
}

Now, the appearance and removal of the white background color eases in and out over 800 milliseconds, which looks cool.

That’s all folks!

That completes our implementation. Keep practicing by extending the functionality. Here are some ideas:

  • Create sections on the page and link each of the menu items to separate sections.
  • Our current view is suitable for large screens. Use media queries to make the page responsive.
  • Implement smooth scrolling so that when the user clicks on a menu item, the page smoothly scrolls to that section rather than immediately jumping to it.

You may download the code from this Github repository. If you prefere copy-paste, here’s the HTML:

<!DOCTYPE html>
<html>
<head>
<title>Sticky navigation bar on scroll</title>
<link rel="stylesheet" href="style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;500;600;700;800&family=Roboto:wght@300&display=swap" rel="stylesheet">
</head>
<body>
<header>
<div class="logo">ECO LIVE</div>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#products">Products</a></li>
<li><a href="#about">About</a></li>
<li><a href="#gallery">Gallery</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
<section class="content">
<p>Global warming is a real phenomenon. It is a threat to our very existence. It is estimated that
by the year 2050, we will be unable to sustain. With unchecked industrialization and commercialization,
we have caused serious harm to plant and animal life on earth.</p>
<p>The global eco system has been badly damaged.
Many creatures are near extinction. The sea temperatures are rising, causing serious threat to the
ocean life. The glaciers are melting at an alarming pace. Yet, we are not acting.
</p>
<p>We are a global consortium of organizations, and individuals campaigning for climate action. We have
started various practical intiatives. This includes tree plantation campaigns, creating animal habitats,
protecting river life systems, and much more. Our various programs have created real measurable impact.
</p>
</section>
<script src="app.js"></script>
</body>
</html>

Here’s the CSS:

*, ::after, ::before {
margin: 0;
padding: 0;
box-sizing: border-box;
}

html {
font-family: 'Poppins', 'Open Sans', sans-serif;
font-size: 62.5%;
}

header {
width: 100%;
height: 100vh;
background: url('globalwarming.jpeg') no-repeat 50% 50%;
background-size: cover;
background-attachment: fixed;
}

.content {
width: 100%;
margin: 2rem auto;
font-size: 2rem;
line-height: 1.5;
text-align: justify;
}

.logo {
position: fixed;
font-weight: 400;
font-size: 3rem;
margin-left: 1rem;
z-index: 2;
}

header nav {
position: fixed;
width: 100%;
z-index: 1;
}

header nav ul {
list-style: none;
text-align: center;
transition: 0.8s ease;
}

header nav.white ul {
background: #FFF;
}

header nav ul li {
display: inline-block;
padding: 2rem;
}

header nav ul li a {
text-decoration: none;
color: rgba(0, 0, 0, 0.85);
font-size: 1.3rem;
font-family: 'Roboto';
text-transform: uppercase;
letter-spacing: 2px;
}

Here’s the JavaScript:

const nav = document.querySelector('header nav')
window.onscroll = () => {
if (window.scrollY > 0) {
nav.classList.add('white')
}
else {
nav.classList.remove('white')
}
}

--

--

Muhammad Saqib Ilyas

A computer science teacher by profession. I love teaching and learning programming. I like to write about frontend development, and coding interview preparation