DEV Community

Khwilo Kabaka
Khwilo Kabaka

Posted on • Updated on

How to create full-screen drawer navigation in HTML, CSS, and JavaScript

Introduction

In this guide, we are going to learn how to create a navigation menu that overlays other elements on a page. We will layout the structure of the page first using HTML, add styling using CSS and then add interactivity (opening and closing of the navigation drawer) using JavaScript. Let's get started.

Document Structure

First, we will add the structure of the page. In the body tag of your HTML, add the header contents as follows:

<header class="header">
  <div class="logo">
    <h1 class="logo__text">Logo</h1>
  </div>
  <button class="menu-toggle" aria-label="Menu toggle button">
    <span class="hamburger"></span>
  </button>
  <nav class="nav">
    <ul class="nav__list">
      <li class="nav__list-item">
        <a href="#home" class="nav__link">Home</a>
      </li>
      <li class="nav__list-item">
        <a href="#about" class="nav__link">About</a>
      </li>
      <li class="nav__list-item">
        <a href="#projects" class="nav__link">Project</a>
      </li>
      <li class="nav__list-item">
        <a href="#contact" class="nav__link">Contact</a>
      </li>
    </ul>
  </nav>
</header>

The header content has a logo, a button representing the hamburger menu and navigation links. Since the button element doesn't contain any text, it's important to add an aria-label associated with it.

The remaining structure will be the main content where one has to navigate to:

<main class="main">
  <div class="main__home" id="home">
    <h2 class="main__section-title">Home</h2>
    <p class="main__section-content">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Officia 
      magnam quod iure ex unde reiciendis adipisci sint saepe nam nostrum 
      quibusdam sit, possimus nihil expedita. Consequatur, consequuntur 
      sint. Necessitatibus, recusandae? Lorem ipsum dolor sit, amet 
      consectetur adipisicing elit. Asperiores, dolorum! Atque, nam vitae 
      qui ab commodi ullam. Mollitia expedita a quae fugit obcaecati nam 
      amet possimus magni natus, sequi vitae. Lorem ipsum, dolor sit amet 
      consectetur adipisicing elit. Possimus, nobis consectetur alias odit 
      laboriosam fugiat voluptas, molestias vitae itaque excepturi 
      perferendis necessitatibus consequuntur deleniti. Quas incidunt 
      officia provident reiciendis sed!
    </p>
  </div>
  <div class="main__about" id="about">
    <h2 class="main__section-title">About</h2>
    <p class="main__section-content">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Officia 
      magnam quod iure ex unde reiciendis adipisci sint saepe nam nostrum 
      quibusdam sit, possimus nihil expedita. Consequatur, consequuntur 
      sint. Necessitatibus, recusandae? Lorem ipsum dolor sit, amet 
      consectetur adipisicing elit. Asperiores, dolorum! Atque, nam vitae 
      qui ab commodi ullam. Mollitia expedita a quae fugit obcaecati nam 
      amet possimus magni natus, sequi vitae. Lorem ipsum, dolor sit amet 
      consectetur adipisicing elit. Possimus, nobis consectetur alias odit 
      laboriosam fugiat voluptas, molestias vitae itaque excepturi 
      perferendis necessitatibus consequuntur deleniti. Quas incidunt 
      officia provident reiciendis sed!
    </p>
  </div>
 <div class="main__projects" id="projects">
    <h2 class="main__section-title">Projects</h2>
    <p class="main__section-content">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Officia 
      magnam quod iure ex unde reiciendis adipisci sint saepe nam nostrum 
      quibusdam sit, possimus nihil expedita. Consequatur, consequuntur 
      sint. Necessitatibus, recusandae? Lorem ipsum dolor sit, amet 
      consectetur adipisicing elit. Asperiores, dolorum! Atque, nam vitae 
      qui ab commodi ullam. Mollitia expedita a quae fugit obcaecati nam 
      amet possimus magni natus, sequi vitae. Lorem ipsum, dolor sit amet 
      consectetur adipisicing elit. Possimus, nobis consectetur alias odit 
      laboriosam fugiat voluptas, molestias vitae itaque excepturi 
      perferendis necessitatibus consequuntur deleniti. Quas incidunt 
      officia provident reiciendis sed!
    </p>
 </div>
 <div class="main__contact" id="contact">
     <h2 class="main__section-title">Contact</h2>
    <p class="main__section-content">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Officia 
      magnam quod iure ex unde reiciendis adipisci sint saepe nam nostrum 
      quibusdam sit, possimus nihil expedita. Consequatur, consequuntur 
      sint. Necessitatibus, recusandae? Lorem ipsum dolor sit, amet 
      consectetur adipisicing elit. Asperiores, dolorum! Atque, nam vitae 
      qui ab commodi ullam. Mollitia expedita a quae fugit obcaecati nam 
      amet possimus magni natus, sequi vitae. Lorem ipsum, dolor sit amet 
      consectetur adipisicing elit. Possimus, nobis consectetur alias odit 
      laboriosam fugiat voluptas, molestias vitae itaque excepturi 
      perferendis necessitatibus consequuntur deleniti. Quas incidunt 
      officia provident reiciendis sed!
    </p>
 </div>
</main>

The main content contains navigation target elements having a stub text.

Styling the contents

Now will be the time to add style to the contents. First, we will add some basic styling:

@import url('https://fonts.googleapis.com/cssfamily=Lato:400,700|Pacifico&display=swap');

:root {
  font-size: calc(0.5em + 1vw);

  --clr-cream: #f6e7c1;
  --clr-light-orange: #ffa25b;
  --clr-orange: #f4722b;
  --clr-dark: #3e3e3e;
  --clr-white: #fff;

  --fw-regular: 400;
  --fw-bold: 700;

  --fs-heading-1: 3em;
  --fs-heading-2: 1.5em;
}

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

body {
  background-color: var(--clr-cream);
  color: var(--clr-dark);
  font-family: 'Lato', sans-serif;
  margin: 0;
}

h1 {
  margin: 0;
}

.logo__text {
  font-family: 'Pacifico', cursive;
  font-size: var(--fs-heading-1);
  color: var(--clr-orange);
}

The above code snippet adds a responsive font, the font families to use and also adds a color palette. To distinguish the main content details, add the following styling:

.main {
  width: 80%;
  margin: 0 auto;
}

.main > * { /* Target direct descendants of the .main class */
  padding: 1em;
}

.main > * + * { /* Target adjacent direct descendants of the .main class */
  margin-top: 1em;
}

.main > *:nth-child(odd) { /* Targets the odd children of the .main class */
  background-color: var(--clr-dark);
}

.main__section-title { /* Section heading */
  font-size: var(--fs-heading-2);
  color: var(--clr-light-orange);
  margin: 0;
  padding: 0;
  display: inline;
  text-decoration: underline;
}

.main__section-content { /* Section content */
  padding: 0;
  margin-top: 0.5em;
}

.main > *:nth-child(odd) > .main__section-title { /* Odd main element child heading */
  background-color: var(--clr-dark);
}

.main > *:nth-child(odd) > .main__section-content { /* Odd main element child content */
  background-color: var(--clr-dark);
  color: var(--clr-white);
}

Style the navigation drawer

Make the header element have a display of flex and let the navigation element position be fixed and bring it to the front by declaring a z-index of 1.

.header {
  display: flex;
  justify-content: space-between;
  padding: 1em;
}

.nav {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: var(--clr-dark);
  z-index: 1;
}

Style the navigation item list by removing the default style type for list items and centering the items on the page. Adding a margin 0 ensures that the background color covers the entire page.

.nav__list {
  list-style-type: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
  height: 100%;
  background-color: var(--clr-dark);
  margin: 0;
}

To make the navigation links visible, we are going to make them have a white color and a font-size of 1.5em. When a user hovers over the links, make the links to have a color of light orange and underline the text. On focus, the links will have an orange color:

.nav__link {
  text-decoration: none;
  padding: 1em 2em;
  background-color: var(--clr-dark);
  color: var(--clr-white);
  font-size: var(--fs-heading-2);
  font-weight: var(--fw-bold);
  padding: 0;
  margin: 0;
}

.nav__link:hover {
  text-decoration: underline;
  color: var(--clr-light-orange);
}

.nav__link:focus {
  color: var(--clr-orange);
}

We are then going to add a hamburger menu using CSS. Position the button element to the top right of the page. Bring it to the front by adding a z-index of 2. Add a background of transparent and remove its border:

.menu-toggle {
  padding: 1.5em;
  background: transparent;
  border: none;
  cursor: pointer;
  position: absolute;
  top: 1em;
  right: 1em;
  z-index: 2;
}

The hamburger is wrapped inside a span in the button element. The span is displayed as a block element. Declare a relative position to the span since we are going to add 3 horizontal bars to it.

.hamburger {
  display: block;
  position: relative;
}

Declare the width, height and background color of the horizontal bars:

.hamburger,
.hamburger::before,
.hamburger::after {
  width: 2.5em;
  height: 2px;
  background-color: var(--clr-orange);
}

The pseudo-elements :before and :after of the .hamburger class represents the bottom and top horizontal bars.

For the to bottom and top horizontal bars, let them have an absolute positioning with a left and right of zero.

.hamburger::before,
.hamburger::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
}

Move the bottom horizontal bar 8px from the top and the top horizontal bar to be 8px from the bottom of the middle horizontal bar.

.hamburger::before {
  top: 8px;
}

.hamburger::after {
  bottom: 8px;
}

Add interactivity

Hide the navigation drawer by moving it further to the right along the x-axis.

.nav {
  transform: translateX(100%);
}

Get the reference of the toggle menu button using JavaScript and add a class of nav-open to the body when the button is clicked.

const toggleMenu = document.querySelector('.menu-toggle');

toggleMenu.addEventListener('click', () => {
  document.body.classList.toggle('nav-open');
});

When the navigation drawer is open, we display the navigation menu by moving it to the view using CSS. Then, have the menu toggle button to be fixed to the top when scrolling.

.nav-open .nav {
  transform: translate(0);
} 

.nav-open .menu-toggle {
  position: fixed;
}

Change the hamburger menu to an X character by rotating the horizontal bars. The hamburger menu is rotated to 45 degrees. The bottom horizontal bar is rotated to 90 degrees and then is aligned by moving it 8px away from the x-axis so that the X character is displayed. This because we declared it to be 8px from the top. The top horizontal bar is set to have an opacity of zero.

.nav-open .hamburger {
  transform: rotate(45deg);
}

.nav-open .hamburger::before {
  transform: rotate(90deg) translateX(-8px);
}

.nav-open .hamburger::after {
  opacity: 0;
}

Currently, when clicking the navigation links the URL changes but we are not being taken to the desired section. To do so, we need to remove the class nav_open from the body when the links are clicked as follows:

const navigationLinks = document.querySelectorAll('.nav__link');

navigationLinks.forEach(link => {
  link.addEventListener('click', () => {
    document.body.classList.remove('nav-open');
  });
});

All navigation links references are fetched from the document then we loop over each link and remove the class nav-open when a link is clicked.

Now the navigation drawer is fully functional.

Bonus

We are going to add simple animation for good user experience. When scrolling, instead of a straight jump to an individual item, make the scroll behavior appear to be gradual. The navigation slides in from the left when the hamburger menu is clicked. The hamburger transitions to an X by accelerating on easing in and decelerates on easing out.

html {
  scroll-behavior: smooth;
}

.nav {
  transition: transform 300ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
}

.hamburger::before,
.hamburger::after {
  transition: transform 300ms ease-in-out;
}

The source code of the application can be found here https://github.com/khwilo/drawer-navigation. Access the live site using the link https://drawer-navigation.netlify.com/.

Top comments (3)

Collapse
 
christvolo1 profile image
Christy Volosin

I love this toggle menu, but the JS part that's supposed to remove the "nav-open" class doesn't work for me. I need it to close the menu after I click the links. It was navigating to the sections before I added that part of JS, so whether I comment out that code block or not, it doesn't affect anything.

Collapse
 
khwilo profile image
Khwilo Kabaka

Hi,

Can you post the JavaScript code snippet that you have written? I would like to know if there's any syntax/logic error in your code.

Collapse
 
christvolo1 profile image
Christy Volosin

Sure. This is my JavaScript.

const navToggle = document.querySelector(".nav-toggle");
const navLinks = document.querySelectorAll(".nav__link");

/This works/
navToggle.addEventListener("click", () => {
document.body.classList.toggle("nav-open");
});

/* Doesn't Work: Supposed to close toggle menu after clicking the menu items */
navToggle.forEach(link => {
link.addEventListener("click", () => {
document.body.classList.remove("nav-open");
});
});