How to Build a Responsive Navigation Bar With Flexbox

How to Build a Responsive Navigation Bar With Flexbox

Flexbox is Perfect for Responsive Navigation

Flexbox is a versatile layout module with which we can create one-dimensional layouts that require flexibility, such as responsive menus. Using flexbox’s ordering, alignment, and sizing properties, we can build navigation bars that adapt their layouts to the viewport size while keeping the HTML outline logical and accessible.

In this tutorial, we’ll look into how to create a responsive navigation bar with flexbox. Our flexbox navigation bar will have three different layouts, depending on the viewport size:

  1. mobile layout in which only the logo and a toggle button will be visible by default and users can open and close the menu using the toggle,
  2. tablet layout in which we will show two call-to-action buttons between the logo and toggle in the default state and the rest of the menu will remain toggleable,
  3. desktop layout in which all the menu items, except for the toggle button, will be visible on the screen.

We will use media queries to detect the viewport size of the user’s browser. Our responsive navigation bar will be mobile-first, so we will create the mobile layout first. Then, we will add the tablet- and desktop-specific CSS using min-width media queries.

The navigation bar will also have a JavaScript-based dropdown submenu that opens and closes when the user clicks the parent menu item.

Here’s how the menu will look on mobile:

Mobile menu with flexboxMobile menu with flexboxMobile menu with flexbox

Here’s the tablet version:

Tablet menu with flexboxTablet menu with flexboxTablet menu with flexbox

And, this is how it will look on desktop:

Desktop menu with flexboxDesktop menu with flexboxDesktop menu with flexbox

You can also test, fork, and play around with the interactive demo on CodePen:

New to Flexbox?

If you aren’t used to flexbox, or need a refresher, these beginner guides will give you all the skills you need to complete this tutorial:

1. Create the HTML for Your Flexbox Navbar

The HTML is a simple <ul> list, as you can see below. The .menu class will be the flex container and the list items will be the flex items. Their order will adapt to the viewport size of the user’s device.

For example, the Log In and Sign Up buttons will come first on mobile, but will be displayed at the end of the menu on desktop. We will achieve this effect by making use of flexbox’s ordering properties.

1
<nav>
2
    <ul class="menu">
3
        <li class="logo"><a href="#">Creative Mind Agency</a></li>
4
        <li class="item"><a href="#">Home</a></li>
5
        <li class="item"><a href="#">About</a></li>
6
        <li class="item has-submenu">
7
            <a tabindex="0">Services</a>
8
            <ul class="submenu">
9
                <li class="subitem"><a href="#">Design</a></li>
10
                <li class="subitem"><a href="#">Development</a></li>
11
                <li class="subitem"><a href="#">SEO</a></li>
12
                <li class="subitem"><a href="#">Copywriting</a></li>
13
            </ul>
14
        </li>
15
        <li class="item has-submenu">
16
            <a tabindex="0">Plans</a>
17
            <ul class="submenu">
18
                <li class="subitem"><a href="#">Freelancer</a></li>
19
                <li class="subitem"><a href="#">Startup</a></li>
20
                <li class="subitem"><a href="#">Enterprise</a></li>
21
            </ul>
22
        </li>
23
        <li class="item"><a href="#">Blog</a></li>
24
        <li class="item"><a href="#">Contact</a>
25
        </li>
26
        <li class="item button"><a href="#">Log In</a></li>
27
        <li class="item button secondary"><a href="#">Sign Up</a></li>
28
        <li class="toggle"><a href="#"><i class="fas fa-bars"></i></a></li>
29
    </ul>
30
</nav>

You have probably noticed that menu items with a submenu (“Services” and “Plans”) have an <a> tag without an href attribute. We do this because these “empty” parent menu items don’t lead to any other page–they just open and close the submenu. Using the anchor tag without href is permitted and prevents the page from jumping up on small screens when the user clicks the empty menu item to open or close the submenu.

We also add the tabindex="0" attribute to <a> elements without a href attribute. This is because empty <a> tags are omitted from the default tab order, so we need to put them back to the tabbing order with the tabindex attribute to keep the menu keyboard-accessible.

Note: the toggle button at the end of the list uses a Font Awesome icon. To make the demo work, you’ll need to add the Font Awesome library to the <head> section of the HTML document from CDN using the code below. (If you want to make the menu work offline, you’ll need to host Font Awesome locally.)

1
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">

2. Add Some Basic Styling to Your Flexbox Navigation Bar

For basic styling, I’ve set some default values and colors, however, you can use any of your own style rules as well:

1
/* Basic styling */
2
* {
3
    box-sizing: border-box;
4
    padding: 0;
5
    margin: 0;
6
}
7
body {
8
    font-family: sans-serif;
9
    font-size: 16px;
10
}
11
nav {
12
    background: #222;
13
    padding: 0 15px;
14
}
15
a {
16
    color: white;
17
    text-decoration: none;
18
}
19
.menu,
20
.submenu {   
21
    list-style-type: none;
22
}
23
.logo {
24
    font-size: 20px;
25
    padding: 7.5px 10px 7.5px 0;
26
}
27
.item {
28
    padding: 10px;
29
}
30
.item.button {
31
    padding: 9px 5px;
32
}
33
.item:not(.button) a:hover,
34
.item a:hover::after {
35
    color: #ccc;
36
}

3. Start With the Mobile Navigation

As our navigation will be mobile-first, we start with the mobile layout. Most responsive flexbox navigation bar menus use column-based layouts for mobile, as menu items can be quickly packed below each other by adding the flex-direction: column; rule to the flex container. Even though this is an excellent solution, we won’t use it in our example. 

Instead, we will create a wrapping, row-based layout for mobile so that we can display the logo and toggle button next to each other on top of the menu.

Mobile layoutMobile layoutMobile layout

The CSS trick here is that we make regular menu items such as Home and About span across the entire container using the width: 100%; rule. So, flexbox will display them below each other, while the logo and toggle will retain their natural sizes and sit on top of the navbar in the same row. 

In the CSS below, we also use the justify-content and align-items properties to align the flex items horizontally and vertically. Besides this, we hide the .item elements using the display: none; rule. Menu items will be only revealed when the user clicks the toggle button. The .active class is not in the HTML code, we will dynamically add it with JavaScript.

1
/* Mobile menu */
2
.menu {
3
    display: flex;
4
    flex-wrap: wrap;
5
    justify-content: space-between;
6
    align-items: center;
7
}
8
.menu li a {
9
    display: block;
10
    padding: 15px 5px;
11
}
12
.menu li.subitem a {
13
    padding: 15px;
14
}
15
.toggle {
16
    order: 1;
17
    font-size: 20px;
18
}
19
.item.button {
20
    order: 2;
21
}
22
.item {
23
    order: 3;
24
    width: 100%;
25
    text-align: center;
26
    display: none;
27
}
28
.active .item {
29
    display: block;
30
}
31
.button.secondary { /* divider between buttons and menu links */
32
    border-bottom: 1px #444 solid;
33
}

As you can see above, we have also changed the order of menu items with the help of the order property. Our HTML outline follows a logical order. This is how we want screen reader users and search engine bots to go through the menu. 

However, in the mobile layout, we want to show the logo and toggle button on top of the menu. We also want to display the two call-to-action buttons (“Log In” and “Sign Up”) before regular menu items. So, we set up the following order:

  • .logo gets the order: 0; value, as it will be the first item (however, as this is the default value of order, we don’t need to add it to the CSS),
  • .toggle gets 1, as it comes right after .logo,
  • .item.button belonging to the Log In and Sign Up buttons gets 2,
  • and .item belonging to the rest of menu items gets 3.

4. Style the Submenu in Your Responsive Navbar

As this is mobile-first navigation, we’ll primarily style the submenu with mobile screens in mind. This is a great technique, as it’s usually harder to create a user-friendly submenu for small screens than for larger ones. Then, we can use the same submenu layout for tablet screens as well. For the desktop, we’ll only need to change the positioning of the submenu.

By default, the submenu is set to display: none; and will be only revealed when the user clicks the parent menu item. We will add the required JavaScript functionality in the next two steps before moving on to the tablet menu.

1
/* Submenu up from mobile screens */
2
.submenu {
3
    display: none;
4
}
5
.submenu-active .submenu {
6
   display: block;
7
}
8
.has-submenu i {
9
    font-size: 12px;
10
}
11
.has-submenu > a::after {
12
    font-family: "Font Awesome 5 Free";
13
    font-size: 12px;
14
    line-height: 16px;
15
    font-weight: 900; 
16
    content: "\f078";
17
    color: white;
18
    padding-left: 5px;
19
}
20
.subitem a {
21
    padding: 10px 15px;
22
}
23
.submenu-active {
24
    background-color: #111;
25
    border-radius: 3px;
26
}

As you can see above, now we add the Font Awesome icons using CSS instead of HTML. These icons we add using the ::after pseudo-element will be the little down arrows shown next to each menu item that has a submenu. 

If you remember we added the Font Awesome icon for the toggle button with HTML in Step 1. This is because the toggle button will be targeted with JavaScript, so it has to be present in the DOM. However, the down arrows here are just style elements that indicate the presence of the submenu. Since no functionality relies on them, it’s better to add them with CSS.

5. Add the Toggle Functionality with JavaScript to Your Flexbox Navbar

We’ll set up the toggle functionality by adding a click event listener to the toggle button that opens and closes the menu on mobile. In the JavaScript code, we will use the ES6 syntax that gives us access to the const and let notation and the for...of loop and already has good browser support.

For the custom JavaScript, create an empty script.js file and add it to the HTML in your responsive navigation menu before the closing </body> tag:

1
<script src="script.js"></script>

And here’s the JavaSript code responsible for the toggle functionality:

1
const toggle = document.querySelector(".toggle");
2
const menu = document.querySelector(".menu");
3

4
/* Toggle mobile menu */
5
function toggleMenu() {
6
    if (menu.classList.contains("active")) {
7
        menu.classList.remove("active");
8
        
9
        // adds the menu (hamburger) icon
10
        toggle.querySelector("a").innerHTML = "<i class=’fas fa-bars’></i>";
11
    } else {
12
        menu.classList.add("active");
13
        
14
        // adds the close (x) icon
15
        toggle.querySelector("a").innerHTML = "<i class=’fas fa-times’></i>";
16
    }
17
}
18

19
/* Event Listener */
20
toggle.addEventListener("click", toggleMenu, false);

  1. First, we select the menu and the toggle button using the querySelector() method so that we can access them with JavaScript. 
  2. Then, we add the custom toggleMenu() function that will be called when the toggle is clicked. 
  3. Lastly, we add the event listener that will be listening to the click event using the addEventListener() method.

6. Add the Dropdown Menu Flexbox Functionality with JavaScript

Now, when the user clicks the toggle button, the menu is activated and deactivated, however, the submenu is still hidden. We will add this functionality with the following JavaScript:

1
const items = document.querySelectorAll(".item");
2

3
/* Activate Submenu */
4
function toggleItem() {
5
  if (this.classList.contains("submenu-active")) {
6
    this.classList.remove("submenu-active");
7
  } else if (menu.querySelector(".submenu-active")) {
8
    menu.querySelector(".submenu-active").classList.remove("submenu-active");
9
    this.classList.add("submenu-active");
10
  } else {
11
    this.classList.add("submenu-active");
12
  }
13
}
14

15
/* Event Listeners */
16
for (let item of items) {
17
    if (item.querySelector(".submenu")) {
18
      item.addEventListener("click", toggleItem, false);
19
      item.addEventListener("keypress", toggleItem, false);
20
    }   
21
}

Here, we add the .submenu-active class to each menu item with a submenu when the user clicks it. 

  1. First, we select all menu items with the querySelectorAll() method that returns a node list (rather than a single element like querySelector()). 
  2. In the custom toggleItem() function, we add and remove .submenu-active to/from the clicked element. Note that in the else if block, we remove the class from every other flex menu item that was previously opened. This way, it won’t happen that two submenus are open at the same time, as they can cover each other on the desktop.
  3. Finally, we loop through the items classList using a for...of loop. Within the if block, we add two event listeners to menu items that have a submenu: one for the click event for regular users who access the menu by clicking or tapping, and one for the keypress event for keyboard users.

7. Create the Tablet Menu with Flexbox

We’ll create the tablet layout using a min-width media query. On a tablet, four menu items will be visible by default: the logo, the two call-to-action buttons (“Log In” and “Sign Up”), and the toggle. To make everything pretty, our CSS will:

  • change the order of the flexbox menu items to adapt the layout to tablet viewports,
  • realign the items (see the explanation below),
  • make the Log In and  Sign Up buttons look like real buttons (in the mobile layout, they look like links, as they are part of the toggleable flexbox responsive dropdown list).

In code:

1
/* Tablet menu */
2
@media all and (min-width: 700px) {
3
    .menu {
4
        justify-content: center;
5
    }
6
    .logo {
7
        flex: 1;
8
    }
9
    .item.button {
10
        width: auto;
11
        order: 1;
12
        display: block;
13
    }
14
    .toggle {
15
        flex: 1;
16
        text-align: right;
17
        order: 2;
18
    }
19
    /* Button up from tablet screen */
20
    .menu li.button a {
21
        padding: 10px 15px;
22
        margin: 5px 0;
23
    }
24
    .button a {
25
        background: #0080ff;
26
        border: 1px royalblue solid;
27
    }
28
    .button.secondary {
29
        border: 0;
30
    }
31
    .button.secondary a {
32
        background: transparent;
33
        border: 1px #0080ff solid;  
34
    }
35
    .button a:hover {
36
        text-decoration: none;
37
    }
38
    .button:not(.secondary) a:hover {
39
        background: royalblue;
40
        border-color: darkblue;
41
    }
42
}

In the tablet layout, menu items are aligned differently. If you take a look at the four visible menu items, you will see that the two buttons are displayed in the center, while the logo and toggle are pushed to the left and right end of the container:

Responsive tablet menuResponsive tablet menuResponsive tablet menu

We can achieve this effect using the flex: 1; CSS rule. The flex property is a shorthand for flex-growflex-shrink, and flex-basis. It can exist with many different value combinations. When it’s declared with only one value, it belongs to flex-grow, with flex-shrink and flex-basis keeping their default values.

In the CSS above, we have added the flex: 1; rule to the .logo and .toggle elements. In this way, we can tell the browser that if there’s any positive space on the screen, we want to share it between these two elements. As the Log In and Sign Up buttons retain their default 0 value for flex-grow, they won’t get anything from the extra space. So, they will stay in the center of the container, as they adhere to the justify-content: center; rule set on the flex container.

8. Create the Desktop Flex Menu

The desktop flexbox navbar menu hides the toggle, sets back the original order and natural width of each item, and repositions the submenu. 

It’s important to keep in mind that the tablet-specific rules also apply to the desktop menu. This is because here, the viewport width is larger than both 700px and 960px, so both media queries take effect. So, .logo retains its flex: 1; property and pushes the rest of the items to the end of the container.

1
/* Desktop menu */
2
@media all and (min-width: 960px) {
3
    .menu {
4
        align-items: flex-start;     
5
        flex-wrap: nowrap;
6
        background: none;
7
    }
8
    .logo {
9
        order: 0;
10
    }
11
    .item {
12
        order: 1;
13
        position: relative;
14
        display: block; 
15
        width: auto;
16
    }
17
    .button {
18
        order: 2;
19
    }
20
    .submenu-active .submenu {
21
        display: block;
22
        position: absolute;
23
        left: 0;
24
        top: 68px;
25
        background: #111;
26
    }
27
    .toggle {
28
        display: none;
29
    }
30
    .submenu-active {
31
        border-radius: 0;
32
    }
33
}

9. Let Users Close the Submenu By Clicking Anywhere on the Page

Now there’s only one step back. As the dropdown menu is activated on the click event, it doesn’t close automatically when the user hovers away from the top menu item. This is especially annoying on desktops where the dropdown menu flexbox can cover the content. 

So, it would be nice to enable users to close the submenu by clicking anywhere on the screen. We can add the feature with JavaScript:

1
/* Close Submenu From Anywhere */
2
function closeSubmenu(e) {
3
  if (menu.querySelector(".submenu-active")) {
4
    let isClickInside = menu
5
      .querySelector(".submenu-active")
6
      .contains(e.target);
7

8
    if (!isClickInside && menu.querySelector(".submenu-active")) {
9
      menu.querySelector(".submenu-active").classList.remove("submenu-active");
10
    }
11
  }
12
}
13

14
/* Event listener */
15
document.addEventListener("click", closeSubmenu, false);

The custom closeSubmenu() function checks if there’s an open submenu on the screen, and if yes, it also checks if the user clicked inside of it with the help of the target property. If the user clicked anywhere else on the screen, the .submenu-active class will be removed, and the submenu will close itself. We add the event listener to the document object, as we want to listen for clicks on the whole page.

You’ve Built a Responsive Navigation Bar With Flexbox and JavaScript!

Our mobile-first, responsive navigation bar with flexbox is up and running in three different layouts. 

Here’s a reminder of the end result:

Flexbox is a great tool to implement complex layouts without any tweaks. If you combine flexbox’s alignment, ordering, and sizing properties with media queries, you can create different layouts for different viewports without having to manipulate the HTML source code.

Useful Resources to Learn How to Create Responsive Menus

For a list of best practices you should consider when building responsive navigation, or if you need help getting started with event listeners in JavaScript, take a look at these beginners’ guides:

Lastly, if you are interested in how you can use flexbox in your everyday work, have a look at these other practical tutorials–each one helps you learn by building things you can actually use:

منبع: https://webdesign.tutsplus.com/how-to-build-a-responsive-navigation-bar-with-flexbox–cms-33535t