DEV Community

Cody
Cody

Posted on

How to build a customizable, reusable & responsive navbar

As a junior developer, I'm always looking for ways to increase my knowledge, even on things I've already learned or on things that might have easier solutions. Getting to know the how & the why behind this stuff is the best way to grow as a dev. One thing I've always struggled with is building a responsive navigation that can be implemented on virtually any site. So I finally dived in and built my own solution.

What I wanted to accomplish

  1. It needed to be responsive on any screen size.
  2. It needed to be customizable to fit any page.

It seems simple, but these two ideas are very important. The modern web is viewed, by a very large percentage, on a mobile device. Mobile responsiveness should absolutely be a priority for any webpage. So, firstly always ensure your site looks great on multiple screen widths. Secondly, I wanted something I could frequently come back to and easily implement in something new I was working on. Yes, this can be done through various ways (this is even inspired by Bulma, which is an amazing modular framework), but for my own understanding as a dev, I wanted to do it myself and have something simple without so much bloat from your typical framework.

The Markup:

First, we're going to create a basic nav menu in HTML:

<header  class="navigation-wrapper">
    <nav  role="navigation"  class="navigation"  id="navBar">
        <div  class="logo-container">
            <a  class="nav-item"  id="navbar-logo">
                <img  class="logo-img" src="https://codywilliamson.com/assets/logo-3.png">
            </a>

            <div  class="nav-burger"  id="navBurger">
                <div  class="burger"></div>
            </div>
        </div>

        <div  class="nav-items"  id="navItems">
            <a  id="navItem"  href="#home"  class="nav-item">Home</a>
            <a  id="navItem"  href="#about"  class="nav-item">About</a>
        </div>
    </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

Now, there are a few things going on here. Disclaimer first: a navbar can be made a million different ways — some are complicated, some are simplistic, some are over-engineered, some are basic. Hint: it doesn't matter. They all yield the same result, mostly.

Anyway, everything is wrapped in a nav.navigation container. If you aren't utilizing semantic HTML yet you are behind the times, my friend. Following this are two important div containers. One, div.logo-container is for the beginning of the nav which could contain a company name, logo, etc. It also contains the markup for our nav-burger, but onto that later.

The second div.nav-items contains our actual menu, which is for the end section of our nav. For this example, only two items are included, but you can add many more. As you can see, this only consists of two a links. A ul > li is perfectly acceptable, but there's no need for ul > li > a CSS in my book, especially for a simple project like this.

The Styles:

This next section is lengthy, but it's not as bad as you think. First off, lets create a quick CSS reset and add some SCSS variables:

// reset found @ [https://alligator.io/css/minimal-css-reset/](https://alligator.io/css/minimal-css-reset/)
html {box-sizing: border-box;font-size: 16px;}
*, *:before, *:after {box-sizing: inherit;}
body, h1, h2, h3, h4, h5, h6, p, ol, ul {margin: 0;padding: 0;font-weight: normal; -webkite-font-smoothing: antialiased; text-rendering: optimizeLegibility;}
ol, ul {list-style: none;}
img {max-width: 100%;height: auto;}
html, body {height: 100%;width: 100;}
a {text-decoration:none;}

// colors
$black: #212121;
$white: #EEEEEE;
$darker-blue: #292E49;
$light-grey: #E0E0E0;

// nav  --  you can add as many of these as you want
//          its really limitless on customization options
$nav-padding: 1rem;
$nav-horizontal-padding: $nav-padding + 1.25rem;
$nav-vertical-padding: $nav-padding;
$nav-mobile-padding: 0.2rem;
$nav-background-color: $light-grey;
$nav-item-color: $black;
$nav-item-padding: 1rem;
$nav-item-margin: 0.5rem;
$nav-item-hover-background: $darker-blue;
$nav-item-border-radius: 5px;
$nav-font-size: 1.3em;
$nav-mobile-breakpoint: 768px;  // change to what you want based on
                                // how many nav items you have
Enter fullscreen mode Exit fullscreen mode

Next thing we want to do is start with our nav.navigation container:

.navigation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-direction: row;
  padding: $nav-vertical-padding $nav-horizontal-padding;
  font-family: Verdana, Arial, Helvetica, sans-serif;
  font-size: $nav-font-size;
  width: 100vw;
  position: fixed;
  top: 0;
  z-index: 5;
  background: $nav-background-color;
  box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.75);
}
Enter fullscreen mode Exit fullscreen mode

Many of the properties here are self-explainable; however, I will point out the most important.

First off, justify-content: space-between places div.logo-container and div.nav-items them on their respective ends (logo-container being at the beginning and nav-items at the end). If you wanted to switch them, add flex-direction: row-reverse. Simple enough.

Secondly, we have:

width: 100vw;
position: fixed;
top: 0;
z-index: 5;
Enter fullscreen mode Exit fullscreen mode

This ensures the nav takes up 100% of the available screen width on any screen size and that it's fixed to the top of the page. As far as the rest, feel free to change to your liking — that's the point!

Now if you've opted for an image to be your company logo, add:

.logo-img {
    width: 100px; // change accordingly, but be wary of spacing
    height: auto; // ensures logo retains aspect ratio
}
Enter fullscreen mode Exit fullscreen mode

Onto nav-items. In this section, we're really making use of our variables we made earlier, so make sure you mess with those to find the values you like.

.nav-item {
  padding: $nav-item-padding;   // creating background size on hover
  margin: $nav-item-margin;     // adding space between the items
  color: $nav-item-color;

  &:hover {
    background: $nav-item-hover-background;
    border-radius: $nav-item-border-radius;
    color: $white;
  }
}

// this styling applies if you chose to have text instead of an img
#navbar-logo {
  color: $nav-item-color;

  &:hover {
    background: none; // set to none if using a logo img
    // if you're using an text change this to
    // $nav-item-hover-background
  }
}

// lastly, let's hide our nav-burger
.nav-burger,
.burger {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

If you paid attention, you'll notice that I didn't specifically style div.nav-items. Because we set flex-direction in our .navigation container, it's done the work for us. On mobile, this will change.

We're almost done! Just a little bit more.

Next begins our mobile styling. I built this desktop first, then scaled down to mobile. So I'm using max-width: in my media query. Hang on tight, this is the last bit! First, we're going to build our a nav-burger:

@media (max-width: $nav-mobile-breakpoint) {
    // hide nav-items on mobile
    .nav-items {
      display: none;
    }
    // time to eat
   .nav-burger {
      display: block;
      width: 40px;
      margin: 0.5em;
   }
   // here we use pseudo elements to add a top & bottom bar
  .nav-burger:after,
  .nav-burger:before,
  .burger {
    background: $nav-item-color;
    border-radius: 3px;
    content: "";
    display: block;
    height: 5px;
    margin: 7px 0;
    transition: all 0.3s ease-in;
  }
   // when active class is added, rotate burger bars into cross
  .nav-burger.active:before {
    transform: translateY(12px) rotate(135deg);
  }

  .nav-burger.active:after {
    transform: translateY(-12px) rotate(-135deg);
  }
  // when active class is added to .nav-burger, hide middle bar
  // since .burger is child of .nav-burger, make sure to
  // select .burger as a child of the .active class
  .active .burger {
    transform: scale(0);
    opacity: 0; 
  }
}
Enter fullscreen mode Exit fullscreen mode

That wasn’t too bad. Now while we’re still in our $nav-mobile-breakpoint media query, let’s tweak a few things to make our nav look good.

 .logo-container { // this contains our logo and nav-burger
    width: 100%; // stretch width across screen
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between; // logo on left, burger on right
    padding: 0; // change to your liking
  }

  .navigation {
    padding: $nav-mobile-padding;
  }

  .navigation.active {
    flex-direction: column; // when 'active class is added, changes nav.navigation to column i.e., logo-container on top, navItems on bottom
  }

  .nav-items.active {
    display: flex;
    flex-direction: column; // show navItems in column view
    text-align: center; // can align either way
    border-top: 1px solid rgba(0, 0, 0, 0.3); // divider
    width: 100%;

    .nav-item:hover {
      background: $dark-grey;
      color: $white;
    }
  }

Enter fullscreen mode Exit fullscreen mode

And… That’s it for our nav styling. Now to add some quick functionality.

You can do this with jQuery, but I prefer plain JavaScript. If you’re feeling fancy, you can also use the checkbox hack with a few extra lines of code and no JavaScript required, but I’ll save that for another post.

One thing to note here: I’m a beginner when it comes to JS. I’m still actively learning and built this to practice. If there’s a better way to implement, by all means drop me a line and let me know!

Here’s our JavaScript:

// enable 'active' class on nav.navigation, .nav-burger, .nav-items
document.addEventListener('DOMContentLoaded', () => {

    // get navigation elements for active class toggle
    let navbar = document.getElementById('navBar');
    let navBurger = document.getElementById('navBurger');
    let navItems = document.getElementById('navItems');
        // get all navItems with id navItem
    let navItem = document.querySelectorAll('#navItem');

    // on nav-burger click, toggle active class
    navBurger.addEventListener('click', () => {
        navbar.classList.toggle('active');
        navBurger.classList.toggle('active');
        navItems.classList.toggle('active');

        // disable body scroll
        document.body.style.overflow = 'hidden';

        // on nav-item click, remove active class and enable body scroll
        navItem.forEach( el => {
            el.addEventListener('click', () => {
                navItems.classList.remove('active');
                navBurger.classList.remove('active');
                navbar.classList.remove('active');
                    // enable scroll on body
                document.body.style.overflow = 'scroll';
            });
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

Now we are finally done with our mobile responsive and customizable navigation. There’s a lot here and I appreciate you reading through this entire blog post. I hope that you learned something useful!

Discussion (1)

Collapse
omhax profile image
Om Hax

Hi, can you make navbar like dev.to?