Juneikerc.com

How to create a hamburger menu with HTML, CSS, and JavaScript.

Featured image of the post: How to create a hamburger menu with HTML, CSS, and JavaScript.

Since the birth of mobile devices, this type of menu has become the most common across the web, and in this tutorial, I will show you how to create one from scratch using a bit of CSS and JavaScript.

HTML Structure for the Responsive Hamburger Menu

html
<header class="main-header">
<a class="main-logo" href="/">
<strong>Logo</strong>
</a>
<nav id="nav" class="main-nav">
<div class="nav-links">
<a class="link-item" href="#">Link1</a>
<a class="link-item" href="#">Link2</a>
<a class="link-item" href="#">Link3</a>
<a class="link-item" href="#">Link4</a>
</div>
</nav>
<button id="button-menu" class="button-menu">
<span></span>
<span></span>
<span></span>
</button>
</header>

Step-by-Step Explanation of Our Responsive Nav Code

  1. We define a classic header with a link that should be the website's logo but for the practical purposes of this tutorial, I'm placing the name of this website. It's important never to use the logo as an h1, for SEO reasons, this tag should be reserved for the main title of the page.
  2. We add a nav tag with a div that will serve as a container for 4 navigation links.
  3. Finally, a button tag with three span elements that will be used to style the CSS menu.

Adding CSS to Style Our Responsive Hamburger Menu

We start by resetting all the document styles.

css
* {
margin: 0;
padding: 0;
}

Now, we apply some simple styles to the header to separate the logo and the navigation.

css
.main-header {
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 0 0.4rem;
}
.main-logo {
color: black;
font-size: 32px;
text-decoration: none;
}

We start working with the nav tag, designing the styles for mobile first. Later, we will add a media query for the desktop design.

With the following CSS, we achieve that the nav takes up the entire space of the browser window when it's open. It also remains fixed when the user scrolls. We don't want the menu to be always visible, so we hide all the navigation from the main window with the property margin-left: -100%;.

We also add a property transition: all 0.2s linear; so that our element doesn't show and hide in an unnatural way. Additionally, we set the z-index to 100 to ensure the navigation is always above the rest of the page elements.

The nav tag will have a semitransparent background color that is different from the background of the navigation link container.

Finally, we declare that when the nav tag has the "show" class, the margin-left becomes 0, making our navigation appear. We'll work all the magic with JavaScript later.

css
.main-nav {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100%;
margin-left: -100%;
transition: all 0.2s linear;
z-index: 100;
background-color: rgba(0, 0, 0, 0.7);
}
.main-nav.show {
margin-left: 0;
}

The link container will have a white background, and we set flex-direction: column; to arrange the links one below the other.

We assign a width of 70% so that the link container doesn't occupy the entire space of the nav. On the other hand, we declare the height as 100% to cover the entire screen.

Finally, some simple styles for each link.

css
.nav-links {
background-color: rgb(250, 250, 250);
display: flex;
flex-direction: column;
width: 70%;
height: 100%;
align-items: center;
justify-content: flex-start;
}
.link-item {
margin-top: 2rem;
color: #444444;
text-decoration: none;
font-weight: bold;
position: relative;
}

We want the menu button to have a z-index of 200 so that it's clickable above the navigation

css
.button-menu {
z-index: 200;
width: 40px;
height: 40px;
border: none;
display: flex;
background: none;
flex-direction: column; /* Pondrá un span debajo del otro*/
align-items: center;
justify-content: center;
cursor: pointer;
}
/*
Dibujamos una línea recta por cada span creando el tipico menu hamburguesa
*/
.button-menu span {
width: 37px;
height: 4px;
margin-bottom: 5px;
position: relative;
background: #444444;
border-radius: 3px;
transform-origin: 4px 0px;
transition: all 0.2s linear;
}
/*
con los span dibujamos un icono de cerrar
para mostrar cuándo la navegación este abierta
*/
.button-menu.close span {
opacity: 1;
transform: rotate(45deg) translate(0px, 0px);
background: #ffffff;
}
.button-menu.close span:nth-child(2) {
transform: rotate(-45deg) translate(-8px, 5px);
}
.button-menu.close span:nth-child(3) {
display: none;
}

Below, along with the entire code, you'll find the media queries to display the menu differently on desktop.

Complete CSS code for our responsive header and menu.

css
* {
margin: 0;
padding: 0;
}
.main-header {
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: white;
padding: 0 0.4rem;
}
.main-logo {
color: black;
font-size: 32px;
text-decoration: none;
}
.main-nav {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100%;
margin-left: -100%;
transition: all 0.2s linear;
background-color: rgba(0, 0, 0, 0.7);
z-index: 100;
}
.main-nav.show {
margin-left: 0;
}
.nav-links {
background-color: rgb(250, 250, 250);
display: flex;
flex-direction: column;
width: 70%;
height: 100%;
align-items: center;
justify-content: flex-start;
}
.link-item {
margin-top: 2rem;
color: #444444;
text-decoration: none;
font-weight: bold;
position: relative;
}
.link-item::after {
position: absolute;
content: "";
background-color: black;
bottom: -5px;
left: 0;
width: 0%;
height: 3px;
transition: 0.3s ease all;
}
.link-item:hover::after {
width: 100%;
}
.button-menu {
z-index: 200;
width: 40px;
height: 40px;
border: none;
display: flex;
background: none;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
}
.button-menu span {
width: 37px;
height: 4px;
margin-bottom: 5px;
position: relative;
background: #444444;
border-radius: 3px;
transform-origin: 4px 0px;
transition: all 0.2s linear;
}
.button-menu.close span {
opacity: 1;
transform: rotate(45deg) translate(0px, 0px);
background: #ffffff;
}
.button-menu.close span:nth-child(2) {
transform: rotate(-45deg) translate(-8px, 5px);
}
.button-menu.close span:nth-child(3) {
display: none;
}
@media screen and (min-width: 768px) {
.button-menu {
display: none;
}
.main-logo {
flex-basis: 30%;
}
.main-nav {
position: static;
margin-left: 0;
flex-basis: 70%;
height: 100%;
}
.nav-links {
width: 100%;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
background: white;
}
.link-item {
display: inline-block;
margin-top: 0;
margin-right: 2rem;
}
}

We finish with a couple of lines of JavaScript to make the menu interactive.

js
// Select the two elements that will be clickable
const toggleButton = document.getElementById("button-menu");
const navWrapper = document.getElementById("nav");
/*
Whenever the button is clicked,
add and remove the necessary classes
to display the menu.
*/
toggleButton.addEventListener("click", () => {
toggleButton.classList.toggle("close");
navWrapper.classList.toggle("show");
});
/*
When a click occurs outside the link container,
the menu should hide.
*/
navWrapper.addEventListener("click", e => {
if (e.target.id === "nav") {
navWrapper.classList.remove("show");
toggleButton.classList.remove("close");
}
});

Result of the hamburger menu on CodePen

Juneiker Castillo freelance web developer

I am Juneiker Castillo, a passionate front-end web developer deeply in love with programming and creating fast, scalable, and modern websites—a JavaScript enthusiast and a React.js lover ⚛️.

About me