How to Make a Responsive Navigation Menu using only CSS

Gabriela Johnson
4 min readJan 2, 2019

--

Do you need to make a navigation menu that becomes a hamburger menu on mobile. Do you want to give it a slick animation when opened? Do you want to do all of this without JS?

You came to the right place. Lets get to it!

(You will need to view the codepen as a full page to see the responsive effects when changing the screen size)

First lets start with the html structure. Go to https://codepen.io/pen/ or however you run your code and paste in this html.

<nav>
<ul>
<li><a href = “#”>Home</a></li>
<li><a href = “#”>About</a></li>
<li><a href = “#”>Portfolio</a></li>
<li><a href = “#”>Blog</a></li>
<li><a href = “#”>Contact</a></li>
</ul>
</nav>

Right now you should just have an ugly list of links. Now enter this css.

html,body{
margin:0;
padding:0;
height:100%;
}
nav{
width:100%;
background:grey;
position:fixed;
top:0;
height:50px;
display:flex;
justify-content:flex-end;
}
nav li{
display:inline;

}
nav a{
text-decoration:none;
color:white;
font-family:arial;
padding:0px 10px;
}

This will create a fixed navigation bar at the top of the screen, with all of the menu links next to each other horizontally at the end of the div.

How this all works

Now that we have our navigation bar, we need to create our mobile menu functionality.

When you click on the three line hamburger icon on a mobile site, the menu items open up vertically and close just as quickly at the tap of a button. This functionality is usually done using JavaScript, but by using the <input type = “checkbox”> element, we can create the same responsive menu.

The checkbox has two states, checked and unchecked. And luckily for us, we can use the pseudo-class selector :checked to style the menu when the checkbox is checked, similar to on click functionality.

We first do this by inserting our checkbox into our html and giving it an id of “nav-button”. However, we don’t want the bland checkbox to be our navigation button, so we will also add a label and give it a “for” attribute so the label will replace the checkbox with the assigned id.

<nav>
<input type = “checkbox” id = “nav-button”>
<label for = "nav-button">&#9776;</label>
<ul>
<li><a href = “#”>Home</a></li>
<li><a href = “#”>About</a></li>
<li><a href = “#”>Portfolio</a></li>
<li><a href = “#”>Blog</a></li>
<li><a href = “#”>Contact</a></li>
</ul>

</nav>

We want to make sure the checkbox isn’t visible until you need it in portrait mode, so add this css to hide the checkbox by default, then we will create the media query. We also want the label hidden too.

#nav-button{
display:none;
}
label{
display:none;
}

Now enter in this CSS for our media query. We will style the list of links to stack on top of each other and take up 100% of the width of the screen. We will also have it hidden below the navigation bar by giving it a height of 0. The label is also now visible using display:block, but only when the device of the screen is less than 736px.

@media only screen 
and (max-width: 736px)
and (orientation: portrait) {

nav{
align-items:center;
}

nav ul{
position:absolute;
top:34px;
width:100%;
background:darkgrey;
height:0;
overflow:hidden;
transition:height 1s;
}

nav li{
display:block;
width:100%;
padding:5px;
}

label{
display:block;
padding:0px 10px;
font-size:30px;
cursor:pointer;
}

label:hover{
color:white;
}

nav a{
width:100%;
display:block;
}

#nav-button:checked ~ ul{
height:140px;
transition: height 1s;
}

nav li:hover{
background:lightgrey;
}
}

The most important piece of CSS is this one right here:

#nav-button:checked ~ ul{
height:140px;
transition: height 1s;
}

Like we said, we are using the :checked pseudo selector to apply styling only when the checkbox is checked. However, to make this apply to another element, we need to use the general sibling combinator.

The general sibling combinator selects the sibling that is after the former element in the parent hierarchy. So in this case, the checkbox, which is identified by the #nav-button id, is the former element, and the ul element is the sibling that is after it in the hierarchy. This only works if they are siblings, so <input>, <label> and <ul> elements are all siblings and direct children of the <nav> element.

When the label for checkbox is now checked, the height of the unordered list will increase to 140px, in a transition of one second. When it is unchecked, it will return to its height of 0 which was set within the media query.

You should have a navigation menu that now opens and closes on mobile using only CSS! Now you can utilize the power of the checkbox and apply this to other projects you create.

~Gabby J

gabrielajohnson.com ⭐

codepen.io/gabrielajohnson ⭐

Galalea ⭐

--

--