Making a responsive navbar with just HTML & CSS Grid

Alright, so here is a simple way of making a responsive navbar using basically just CSS Grid.

I'll be trying to explain everything as detailed, easy to read, and concise as possible, and if you're new to this, I suggest you pay extra attention to how and why everything is done.

Hopefully, by the end of this, you'll not only be able to recreate a menu just like the one I'm show you how to make, but you'll understand how to make changes to fit your exact needs.

Enough talk! Below, you'll see the exact menu we'll be making, in all its CSS Grid and responsive glory.

Step 1: Defining The Navbar

The first thing we want to do is define our navbar.


  <div class="nav-container">
      <nav></nav>
  </div>
      

The nav-container div is the navbar itself, ie. the strip that runs across the top of the page. The <nav></nav> tags inside it will contain the actual menu items. We want our nav to stretch across the whole page. So, let's set nav-container width to reflect that.


  .nav-container {
    width: 100%;
  }
  

As of right now, our navbar stretches across the horizontal page, and has 0 height. Perfect!

Our navbar now fits all screens in width, but if someone accesses our site on a very large monitor, we don't want our actual menu options to stretch across the entire space. We want to contain them within a certain width.

To do this, we're going to set our "nav" to max-width: 1720px.


  nav {
    max-width: 1720px;
    margin: 0 auto;
  }
      

The margin ensures that our menu is always centered within the navbar, so if someone enters our site on a screen with a width greater than 1720px, our navbar will react like this:

Step 2: Placing elements in the navbar

Time to start making our navbar actually look like a navbar.

We're going to use css grid to split up our nav into appropriate sections.


  nav {
    max-width: 1720px;
    margin: 0 auto;

    display: grid;
    grid-template-columns: 1fr auto 1fr;
  }
      

If you're new to css grid, this next part might be a bit tricky to understand, but just bare with me! I promise it's actually very simple.

By using display: grid, we now have access to css grid properties, like grid-template-columns.

grid-template-columns allows us to define the columns in our container. In our case, we're defining 3 columns: 1fr auto 1fr.

For the sake of understanding, had we done grid-template-columns: 500px 500px 500px, then we would end up with 3 columns 500px wide each. But we're defining ours using fr and auto.

fr in css grid stands for fractional, but a more simple word to remember it by is "free space". 1fr is dynamic,and takes up all the free space available within the container. If we did "grid-template-columns: 1fr 1fr", then we get 2 equally wide columns that look like this:

We defined a column of "auto" between two fr's, and a column of auto means that it automatically sizes itself to fit whatever's inside it.

Because our columns are empty at the moment, the image above actually represents our current nav, if you imagine a 0 width "auto" column in between the two 1fr's.

The point of having a column of auto between two 1fr's, is because we want our logo to go in the center (auto column). We want our menu items to go in the left 1fr, and the right fr will be blank. BUT, the right fr is not useless just because it's blank: we need it there so that the auto column logo in the center will always be exactly in the center, between two 1fr's. The 1fr's will adjust their width to fit the size of the screen, but the auto will remain the same size, as long as the logo inside it does too.

Let's actually code this now:


  <div class="nav-container">
      <nav>
        <div class="left-menu">
        </div>
        <a href="#" class="logo">COSTELLO</a>
      </nav>
  </div>
        

We've added a "left-menu" div and an "a href" for our logo. The "left-menu" div will contain all of our menu options, and the logo-link is given a class of logo so that we can style it with css.

If you've coded along, you should notice that the logo has automatically centered itself! This is because when we defined our 3 columns (1fr auto 1fr), each direct child of the container will be placed accordingly into the columns. So, because our "left-menu" div comes first in the HTML, it gets placed into the first "1fr" column. Our "logo" comes next in the HTML, so it was placed in the next column, which is the "auto" column. If we add another element, it will be placed in the 3rd column, which is the right "1fr".

So, css grid is automatically placing our items into the columns, and it's exactly how we want it right now. But, for safety, and also just for learning, we're going to explicitly define which columns should contain what.


  .left-menu {
    grid-column: 1;
  }

  .logo {
    grid-column: 2;
  }
              

What's great about css grid is how self-explanatory it is! We want the "left-menu" div to always be in the first column, and that's what "grid-column: 1;" takes care of. Likewise, the ".logo" element will always be in the 2nd column.

This probably wasn't necessary in our case, because the auto-distribution of columns was already placing our elements in the right columns, but it's good to have as a safety. If, for instance, we wanted our menu-items to be on the right side of the logo, it wouldn't work to just move around their places in the HTML, because it would move the logo and everything around. Now, however, we know that if we want the menu-items to the right, we just need to change the grid-column of ".left-menu" to 3.

Step 3: Adding menu items

Let's add our menu items.


  <div class="nav-container">
      <nav>
        <div class="left-menu">
           <a href="#">Shop</a>
           <a href="#">Blog</a>
           <a href="#">About</a>
           <a href="#">Contact</a>
        </div>
        <a href="#" class="logo">COSTELLO</a>
      </nav>
  </div>
              

We now have an ugly navbar that's somewhat responsive. Try to resize your browser window, and watch as the menu expands or contracts to fit the screen!

But it's not mobile friendly yet. If the screen is small, the menu bunches up, and we want to fix this.

Step 4: Creating the mobile-friendly dropdown menu

Let's start by downloading a (hamburger) menu-icon. You can find one yourself, or use the one I'm using here. Go ahead and download the 64pixel version in white (because we're using a black background).

Because we're not using any Javascript in this, we're going to do a little trick.

First, let's add to our html:


<div class="nav-container">
    <nav>
      <input type="checkbox" id="nav-toggle">
      <label for="nav-toggle" class="burger-menu">
        <img src="media/menu.png" alt="">
      </label>
      <div class="left-menu">
          <a href="#">Shop</a>
          <a href="#">Blog</a>
          <a href="#">About</a>
          <a href="#">Contact</a>
      </div>
      <a href="#" class="logo">COSTELLO</a>
    </nav>
</div>
            

We've added an input of type=checkbox, and a label with for="nav-toggle".

The checkbox has an id of "nav-toggle", so when we add the label under with "for=nav-toggle", the label is now linked to the checkbox. This does so that if we click on the label, the checkbox gets clicked.

We're doing this, because we want the checkbox to decide if our dropdown menu should be shown or not. If it's checked, the menu should show, and if it's not, the menu should be hidden.

Within the label tags, we're adding our burger icon.

Just a quick note: If you're coding along, go ahead and add a black background to your ".nav-container". This will make it easier to visualize what's going on.


  .nav-container {
    width: 100%;
    background: black;
  }
                

Our menu should look like this now:

Ignore the fact that it looks messed up now. Try clicking on the burger icon. If you've done it properly, the checkbox should get checked!

Let's fix the menu to get it working.

Firstly, we don't want the checkbox or the burger icon to show in full-size mode.


  #nav-toggle, .burger-menu {
    display: hidden;
  }
                

The checkbox and burger icon is now hidden, and our navbar looks normal again.

We want our menu to switch over to mobile-mode when the screensize is less than 1025px. Let's make the burger icon show when the menu is in mobile-mode, and for the menu-left to disappear.


  @media only screen and (max-width: 1025px) {
    .burger-menu {
      display: inline-block;
    }

    .left-menu {
      display: none;
    }
  }
                

The burger icon is way too big, so let's change that and make it 40x40pixels.


  .burger-menu {
    width: 40px;
  }
                

Much better! This actually looks like a fully responsive navbar now!

Except...it doesn't work. Let's change that.

So, remember that when we click on the burger icon, we're actually also clicking on the checkbox. The checkbox is display:hidden, but it's still there and operating.

When the checkbox is checked, ie. when the burger logo is clicked once, we want the menu to appear. We can achieve this in css using this code:


  @media only screen and (max-width: 1025px) {
    .burger-menu {
      display: inline-block;
    }

    .left-menu {
      display: none;
    }

    #nav-toggle:checked ~ .left-menu {
      display: grid;
      grid-row: 2;
    }
  }                
                

Tada! This is now a fully functioning responsive navbar!

Try clicking the burger menu a couple of times, and you'll see that it opens and closes to the click.

Let me explain the css grid above:

#nav-toggle:checked ~ .left-menu. With this selector, when #nav-toggle is checked, it selects all neighbours with a class of "left-menu".

Our "left-menu" has "display: hidden" currently, but we overwrite this by writing "display:grid".

Because we're not specifying any columns like we did above when we used grid, each element in left-menu becomes a row that takes up the entire width. This is default behaviour of a grid container. In our case, it makes our menu look like a list.

We have to specify "grid-row: 2", because the parent container is a grid with the columns "1fr auto 1fr". The burger icon is taking up the first "1fr" column. The logo is of course taking up the "auto" column. That leaves us with only one empty column, which is the right "1fr" column. But we don't want our menu there!

By specifying that we want it to be in row 2, the grid creates another identicle row of "1fr auto 1fr". Our "left-menu" is the only item on row 2, so it gets placed in the first "1fr", directly below the burger icon.

You'll notice that the 2nd row is much taller than the 1st row. This is because we haven't specified any row-dimensions, so they are defaulted to "auto", which we know means "fit content". Our menu list is tall, so it takes up more space than anything in the first row.

That's pretty much it! Keep reading if you want to see how I style it.

Step 5: Styling

Let's start by making the navbar font white and uppercase. I'm also going to add a different font. I'm using the free font 'Oswald' from Google Fonts.


  nav a {
    color: white;
    text-decoration: none;
    text-transform: uppercase;
    font-family: 'Oswald', sans-serif;
  }
                

Next, we want to pretty up the left-menu a bit.


  .left-menu a {
    padding: 10px 0;
    margin-left: 15px;
    font-size: 14px;
    font-weight: 300;
    letter-spacing: 0.5px;
  }
                

The only noteworthy thing to say about the code above, is that I'm adding padding to the top and bottom of each left-menu link. This does so that we get some space above and below the menu option where our mouse can click on it. This is extra useful for when we add a hover effect at the end.

The logo will be the tallest element in our navbar (1st row), so we should style it now, so we can see what kind of height we get for our left-menu.


  .logo {
    font-size: 40px;
    padding: 1rem;
  }
                

Wow, what a difference! Our navbar is now much taller, and it looks much better.

Let's keep going!


  .left-menu {
    grid-column: 1;
    align-self: center;
  }
                

align-self is a lovely css property we get to use because the container is a grid. With it, our left-menu is now centered in the navbar.

Just a couple more things left.

Let's place the burger icon better.


  .burger-menu {
    grid-column: 1;
    align-self: center;
    margin-left: 20px;
  }
                

Perfect!

One last thing: let's add our hover effect, to take it to the next level.


  nav a {
    color: white;
    text-decoration: none;
    text-transform: uppercase;
    font-family: 'Oswald', sans-serif;
    
    transition: .3s all ease-in-out;
  }
                

All we did in the code above was add the transition code. We want "all" specified transitions to take .3s, and to use the smooth ease-in-out animation.

Let's specify the one transition we want to use.


  nav a:hover {
    opacity: .7;
  }
                

When you hover the mouse over any menu item, it now has a cool hover-effect.

Hope you enjoyed the tutorial. Please contact me if you have any questions, or want me to make any more tutorials.