Learn How to Build Responsive Navigation Bar with HTML, CSS, and JavaScript
Tags: webdev, javascript, css, frontend
Published: 2023-06-30 11:19:04

A responsive navigation bar is essential for any web application, whether you are building static websites using the frontend trio (HTML, CSS, JS) or technologies such as React and Angular for single-page apps, it is crucial to know how to build a well-designed navigation bar.

This step-by-step tutorial will give you a clean walk-through, and a straightforward approach to building a responsive navigation bar that adapts to different screen sizes and provides a clean browsing experience for your users.

If you are a beginner, it is important to understand that when building with HTML, it is advisable to avoid using div containers excessively. Instead, you should adopt a semantic approach.

To ensure clarity, let's break down the entire tutorial into a series of steps.

##Step 1 (HTML structure)
Create the complete HTML structure.

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Navigation Bar</title>
</head>
<body>
    
</body>
</html>
```
Remember, all the content of a web page is enclosed within the `<body>` element. Therefore, we will begin by nesting a `<header>` element within the `<body>` element. This `<header>` element will be the parent container for the `<nav>` element.

##Step 2 (Create header and nav element)
Create a `<header>` element within the body and assign it a class name of **"navigation-header"**. Next, nest a `<nav>` element within the `<header>`. Inside the `<nav>`, include two `<div>` containers.
The first div, with a class name of **"logo-container"**, will contain the logo, while the second div, with a class name of **"navigation-items"** will contain the navigation items. Also, give the navigation items container an **id** of **"navigation-items"**.

Nest `<a></a>` within the logo container, the content of the anchor tag can either be text or an image, depending on the requirement of your project. 
Nest three `<a></a>` within the navigation items container. The text content of each anchor element will be based on the requirements of your project. For the sake of this tutorial, I will use "About", "Projects", and "Contact Me" as the text contents for each anchor element respectively.

Lastly, for the HTML part of this tutorial, you will nest another div container within the nav element. This container will host the hamburger icons that will be displayed on smaller screens. Give it a class name of **"hamburger"**. Then, nest two spans within it. The first span should have an id of **"openHam"** with a hamburger HTML entity as its text content `&#9776;`. The second span should have an id of **"closeHam"** with a HTML entity `&#x2716;` as its text content.

###Complete HTML Code Snippet
```
 <header class="navigation-header">
    <nav>
        <div class="logo-container">
            <a href="./index.html">Logo</a>
        </div>
        <div class="navigation-items" id="navigation-items">
            <a href="">About</a>
            <a href="">Projects</a>
            <a href="">Contact Me</a>
        </div>
        <div class="hamburger">
            <span id="openHam">&#9776;</span>
            <span id="closeHam">&#x2716;</span>
        </div>
    </nav>
   </header>
```

##Step 3 (Styling with CSS)
Before proceeding to styling the HTML elements in the external CSS file, ensure the CSS file is attached to the HTML file within the head element using the <link /> tag.

```
<link rel="stylesheet" href="style.css">
```
It is a common practice in web development to apply a CSS reset or CSS normalization to ensure a consistent starting point for styling across different browsers. By resetting the padding, margin, and box-sizing properties of all elements, as well as the body element, you can avoid any inconsistencies or unexpected default styles that different browsers may apply.

####- Create a consistent styling foundation

```
*, body {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
```
The CSS snippet above helps to create a more predictable layout and styling behavior across different browsers and devices.

###- Styling the navigation header
Select the header element using its class name. Since it is the parent container for the `<nav>`, it is going to be the main background, and would have a definite height.
The navigation element within it should be centered on the x-axis and y-axis, therefore, flex properties come into play.

```
.navigation-header {
  background-color: rgb(73, 51, 153);
  padding: 0 15px;
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
}
```
###- Styling the **nav**
Since we are working with only one `<nav>` element, we do not need to give it a class name. Select the nav element using its element name.
If the project you are working requires a uniform width on all parent containers, then you are going to set the max-width of the nav to that required max-width of your project.

For the sake of this project, we are going to set the max-width to 1200px. To make the container stretch to the full max-width, apply 100% width. This way, the container stretches up to the max-width but won't exceed the maximum width making the container responsive in that process.
There are three child containers within the nav, they need to be on the same plain on the x-axis, therefore, we are going to make use of the flex properties to space out the containers and align them properly using the snippet below.

```
nav {
  width: 100%;
  max-width: 1200px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
```
###- Styling the logo
Select the anchor element within the logo container. Set its text color to `ghostwhite`. Remove any underlining or decoration from the text within the `<a>` element. Set the font-weight to bold (700 is the numeric value for bold font-weight). Set the font-size to 26 pixels.

```
.logo-container > a {
  color: ghostwhite;
  text-decoration: none;
  font-weight: 700;
  font-size: 26px;
}
```

#### - Styling the navigation items
Select the navigation items using its class name. Within the navigation items, we have three navigation links, make them align horizontally using the flex layout.

```
.navigation-items {
  display: flex;
  gap: 40px;
}
```
###- Style the navigation links
Select the links within the navigation items using the direction child selector. Set the text color to "ghostwhite", remove text decoration, apply a font weight of 500, and set the font size to 16 pixels. Also add a transition for smooth hover effect.

```
.navigation-items > a {
  color: ghostwhite;
  text-decoration: none;
  font-weight: 500;
  font-size: 16px;
  transition: .4s ease-in-out;
}
```
###- Add hover effect to navigation links
Simply change the color of the navigation to white, or based on the requirement of your project.
```
.navigation-items > a:hover {
  color: white;
}
```

###- Styling the hamburger
The hamburger has two child elements within it which includes the openHam and the closeHam icons. Here we used HTML entities for the icons, you can import icons from icon libraries such as font awesome, google fonts or iconicon library.
On desktop and laptop screens, we definitely do not want the hamburger icons to show, so we hide them by default, then make them show up on smaller screens using media queries. But we are going to add some other default styling to the hamburger.

```
.hamburger {
  display: none;
  font-size: 20px;
  font-weight: 800;
  color: white;
}
```

We are done with the desktop version of the entire navigation. Now, let's proceed to making it responsive and well designed for the smaller screens using media query.

#### - Create a different style for hamburger, and the navigation items on smaller screens using CSS media query

Here, we are going to have a different style from tablet devices with **screen sizes** from **768px** below, this would also cover for mobile screens.

```
@media screen and (max-width:768px) { }
```
Media query is commonly used for creating responsive designs that adapt to smaller screen sizes, such as tablets or smaller desktop screens. In the above code snippet, the styles within the media query will only be applied when the screen width is 768 pixels or smaller. Elements are reselected within the curly braces and are styled to fit properly to the current screen size as written in the media query which is 768px in this case.

The hamburger and the navigation items will be reselected and styled to fit properly for the 768px screens

```
@media screen and (max-width:768px) {
  .hamburger {
    display: flex;
    cursor: pointer;
  }
  .hamburger #closeHam {
    display: none;
  }
}
```
The closeHam icon is hidden and should only display when the navigation menu is displayed.

#### - style the navigation-items within the media query

```
.navigation-items {
    display: none;
    flex-direction: column;
    align-items: center;
    position: absolute;
    right: 0;
    top: 58px;
    background-color: rgb(73, 51, 153);
    width: 100%;
    height: calc(100vh - 58px);
    padding-top: 60px;
    gap: 10vh;
  }
```
It is hidden by default using the **display: none;** property and should only show up when the openHam icon is clicked.
We are going to handle this event logic using JavaScript

#### Complete CSS Code
```
*, body {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: sans-serif;
}

/* Navigation */
.navigation-header {
  background-color: rgb(73, 51, 153);
  padding: 0 15px;
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
}

nav {
  width: 100%;
  max-width: 1200px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.logo-container > a {
  color: ghostwhite;
  text-decoration: none;
  font-weight: 700;
  font-size: 26px;
}
.navigation-items {
  display: flex;
  gap: 40px;
}
.navigation-items > a {
  color: ghostwhite;
  text-decoration: none;
  font-weight: 500;
  font-size: 16px;
  transition: .4s ease-in-out;
}
.navigation-items > a:hover {
  color: white;
}
.hamburger {
  display: none;
  font-size: 20px;
  font-weight: 800;
  color: white;
}
@media screen and (max-width:768px) {
  .hamburger {
    display: flex;
    cursor: pointer;
  }
  .hamburger #closeHam {
    display: none;
  }
  .navigation-items {
    display: none;
    flex-direction: column;
    align-items: center;
    position: absolute;
    right: 0;
    top: 58px;
    background-color: rgb(73, 51, 153);
    width: 100%;
    height: calc(100vh - 58px);
    padding-top: 60px;
    gap: 10vh;
  }
}
```

### Step 4 Click event logic with JavaScript

#### - Variables for openHam, closeHam and navigationItems 
Create openHam, closeHam, and navigationItems variables and assign to elements in the HTML document using their corresponding IDs.

```
let openHam = document.querySelector('#openHam');
let closeHam = document.querySelector('#closeHam');
let navigationItems = document.querySelector('#navigation-items');
```
####- Create a reusable function that controls visibility of elements.

```
const hamburgerEvent = (navigation, close, open) => {
    navigationItems.style.display = navigation;
    closeHam.style.display = close;
    openHam.style.display = open;
};
```
The code snippet defines a function called hamburgerEvent which takes three parameters: navigation, close, and open. Inside the function, it modifies the CSS display property of the navigationItems, closeHam, and openHam elements based on the provided values.

Using an event listener, attach a click event to openHam and closeHam icons, with the hamburgerEvent function as the second parameter of the event listener:

```
openHam.addEventListener('click', () => hamburgerEvent("flex", "block", "none"));
closeHam.addEventListener('click', () => hamburgerEvent("none", "none", "block"));
```
`openHam.addEventListener('click', () => hamburgerEvent("flex", "block", "none"));` sets up an event listener for the click event on the **openHam** icon. When this element is clicked, it triggers an anonymous arrow function that calls the **hamburgerEvent** function with the arguments "flex", "block", and "none". This will change the display property of **navigationItems** to "flex", closeHam to "block", and openHam to "none", effectively showing the navigation items and switching the hamburger icon to the close icon. 

The event listener attached to the closeHam does the exact opposite of this by passing "none", "none", "block" as the parameters of the hamburgerEvent function invoked in the event listener.

###Complete JavaScript Code
```
let openHam = document.querySelector('#openHam');
let closeHam = document.querySelector('#closeHam');
let navigationItems = document.querySelector('#navigation-items');

const hamburgerEvent = (navigation, close, open) => {
    navigationItems.style.display = navigation;
    closeHam.style.display = close;
    openHam.style.display = open;
};

openHam.addEventListener('click', () => hamburgerEvent("flex", "block", "none"));
closeHam.addEventListener('click', () => hamburgerEvent("none", "none", "block"));
```