Ivy Walobwa Ivy is a Flutter developer and technical writer who is interested in creating awesome developer experiences. She is currently a Program Assistant for the Google Africa Developer Scholarship (GADS), where she helps facilitate the learning of students across Africa.

Creating a responsive mobile menu with CSS without JavaScript

8 min read 2479

CSS Create Responsive Mobile Menu

A clear, concise, and intuitive navigation menu is essential for an optimized website user experience. The responsiveness of the menu is a key factor as well. As of this writing, almost 59 percent of web traffic worldwide is attributed to mobile.

With mobile-first responsive design, developers start with the smallest screen size and then gradually scale up, adding add more features and functionality for larger screen sizes. The resulting webpages will automatically adjust to the size of the user’s browser window.

However, as important as a responsive menu is to a website’s UX, it’s not necessary to build it in JavaScript. This tutorial will review how to create a mobile-first responsive menu using only HTML and CSS.

Jump ahead:

CSS-only responsive mobile menu

There are many techniques available for building responsive mobile menus. One common practice is to use pure CSS without one single line of JavaScript. This technique involves employing a simple HTML list structure to develop a menu of links that can be styled and rendered differently based on a device’s screen size.

In this tutorial, we’ll use CSS to build the below responsive menu for mobile, tablet, and desktop:

For smaller screens, the user must click on a hamburger icon to reveal the menu items. Larger screens will display the menu items inline in the navigation bar.

In this tutorial, we’ll build a responsive menu, including a hamburger icon, entirely from pure HTML and CSS. Our end result will look like so:

CSS Menu Mobile View Hamburger Menu Open
Mobile view, with hamburger icon opened to reveal the menu items.

Let’s try it out!

Getting started

Using your favorite text editor, such as VS Code, create two files in one common folder:

  • index.html for HTML code
  • style.css for CSS code

Copy the index.html file path and paste it into a browser to preview the app.

Adding the HTML

Add the following code to the index.html file:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- App title -->
    <title>Responsive Pure CSS Menu</title>
    <!-- Link CSS file -->
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <!-- Navigation bar -->
    <header class="header">
        <!-- Logo -->
        <a href="#" class="logo">LR</a>
        <!-- Hamburger icon -->
        <input class="side-menu" type="checkbox" id="side-menu"/>
        <label class="hamb" for="side-menu"><span class="hamb-line"></span></label>
        <!-- Menu -->
        <nav class="nav">
            <ul class="menu">
                <li><a href="#">Gallery</a></li>
                <li><a href="#">Blog</a> </li>
                <li><a href="#">About</a></li>
            </ul>
        </nav>
    </header>
    <!-- Main content -->
    <main>
        <article>
            <h1>
                Some content
            </h1>
            <p>
                More Content
            </p>
        </article>
    </main>
</body>
</html>

This code contains the structure and content of the web page. It also includes a reference to the CSS style sheet.

We use the header and main semantic tags to separate the navigation bar and the main content of the page.

We add a logo using the <a> anchor tag.

Lastly, we create a hamburger menu using a checkbox hack. With this strategy, we can style the menu according to whether the checkbox is checked.



We use a label tag to define the hamburger menu icon. The input tag is used to conditionally display the menu depending on the state of the checkbox (class side-menu).

Then, we add the menu items as link list elements, <li>, in an unordered list, ul. The nav tag serves as the list container.

Here’s the output with HTML only:

Mobile Menu HTML Only

Adding the CSS

Now, we’ll use CSS to style the different UI components and features:

Styling the content and background

We’ll add the following code to the style.css file to adjust the appearance of the HTML content:

/* Theming */
@import url("https://fonts.googleapis.com/css2?family=Poppins:[email protected];700&display=swap"); /* import font */

:root{
    --white: #f9f9f9;
    --black: #36383F;
    --gray: #85888C;
} /* variables*/

/* Reset */
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body{
    background-color: var(--white);
    font-family: "Poppins", sans-serif;
}
a{
    text-decoration: none;
}
ul{
    list-style: none;
}

This code imports the Poppins Google font for use in the app.

We define the CSS variables for the colors to be used in the app. Then, we use CSS reset to remove the browser’s default settings for margin , padding , box-sizing, text-decoration, and list-style.

We also specify a white background-color and the Poppins font-family for the page content.

Here’s the output, displaying the styled content and background:

CSS Menu Styled Content Background

The code below adds a black background-color and gray box-shadow to the header. To keep the header at the top of the screen during scrolling, we specify a sticky position and a zero offset from the top. We also adjust the header to stretch across the full width of the device:

/* Header */
.header{
    background-color: var(--black);
    box-shadow: 1px 1px 5px 0px var(--gray);
    position: sticky;
    top: 0;
    width: 100%;
}
/* Logo */
.logo{
    display: inline-block;
    color: var(--white);
    font-size: 60px;
    margin-left: 10px;
}

We style the logo by specifying the color, font-size, and left-margin. Not to be confused with padding, the [margin]is the area around the logo that separates it from other elements.

Here’s the output, displaying the styled header and logo:

CSS Styled Header Logo

Styling the navigation menu

In the code below, we specify width and height properties of 100 percent for the nav element in order to fit the content to the screen. Then, we specify a fixed position to overlay the navigation menu on top of the main app content. We also select a black background-color for the nav element and specify that any overflow content from the nav element should be hidden.

For the menu link elements, we specify a block format display, add padding and color, and change the background-color from white to gray on hover.

Lastly, we use the CSS [transition] property and a max-height of zero to hide the nav element by default but reveal it when the menu icon is clicked:

/* Nav menu */
.nav{
    width: 100%;
    height: 100%;
    position: fixed;
    background-color: var(--black);
    overflow: hidden;

}
.menu a{
    display: block;
    padding: 30px;
    color: var(--white);
}
.menu a:hover{
    background-color: var(--gray);
}
.nav{
    max-height: 0;
    transition: max-height .5s ease-out;
}

Here’s the output, displaying the styled navigation menu:

CSS Styled Navigation Menu

Styling the hamburger menu

In the code below, we specify that a pointer cursor should be displayed when a user interacts with the hamburger menu. We position the label element to the right and add some padding.

Next, we style the span element to create the three menu icon lines.

We use the CSS pseudo-elements [::before] and [::after] on the span element to define the three hamburger icon lines.

The .hamb-line selector defines the center — or second — line. The .hamb-line::before and .hamb-line::after position the first and third lines 5px above and below the center line, respectively.

Lastly, we use the display property on .side-menu to hide the checkbox:

/* Menu Icon */
.hamb{
    cursor: pointer;
    float: right;
    padding: 40px 20px;
}/* Style label tag */

.hamb-line {
    background: var(--white);
    display: block;
    height: 2px;
    position: relative;
    width: 24px;

} /* Style span tag */

.hamb-line::before,
.hamb-line::after{
    background: var(--white);
    content: '';
    display: block;
    height: 100%;
    position: absolute;
    transition: all .2s ease-out;
    width: 100%;
}
.hamb-line::before{
    top: 5px;
}
.hamb-line::after{
    top: -5px;
}

.side-menu {
    display: none;
} /* Hide checkbox */

Here’s the output, displaying the styled hamburger menu:

CSS Styled Hamburger Menu

Styling the toggled menu icon

In the code below, we style the hamburger menu icon to alter its appearance when checked. First, we specify the max-height of the nav element when the checkbox is checked (class .side-menu:checked).

Then, we follow a two-step process to create an “x”-shaped close icon to indicate that the checkbox is checked. First, we hide the second line of the hamburger icon by setting its background to transparent. Then, we rotate the first and third lines by -45 and 45 degrees, respectively, to form an “x” shape:

/* Toggle menu icon */
.side-menu:checked ~ nav{
    max-height: 100%;
}
.side-menu:checked ~ .hamb .hamb-line {
    background: transparent;
}
.side-menu:checked ~ .hamb .hamb-line::before {
    transform: rotate(-45deg);
    top:0;
}
.side-menu:checked ~ .hamb .hamb-line::after {
    transform: rotate(45deg);
    top:0;
}

Here’s the output, displaying the toggled menu:

Adding responsiveness

We can make the app responsive by using media queries to include CSS properties conditionally. In other words, the properties inside a media query will be applied to the web page only when the condition set is valid.

/* Responsiveness */
@media (min-width: 768px) {
    .nav{
        max-height: none;
        top: 0;
        position: relative;
        float: right;
        width: fit-content;
        background-color: transparent;
    }
    .menu li{
        float: left;
    }
    .menu a:hover{
        background-color: transparent;
        color: var(--gray);

    }

    .hamb{
        display: none;
    }
}

In this code, we add a @media rule with the device condition set to a 768px min-width. We want devices with this minimum width to see the full navigation menu, rather than the hamburger menu.

We remove the max-height property of the nav element by setting it to none.

We position the nav element at the top-right of the screen and specify its width to fit-content.

We float the menu list items to the left of the nav. We specify the background color to be transparent and the menu list items to be gray on hover.

Lastly, we use the display property to hide the hamburger menu icon.

Here’s the fully styled app:

CSS Menu Fully Styled App

This video demonstrates the app’s responsive user interface:

Fixed vs. relative vs. sticky navigation menu

The CSS position property can be used to position the navigation menu on a webpage. The top, right, bottom, and left properties can be used to position an element on the page.

With fixed positioning, the navigation menu will remain in the same position despite scrolling. This might cause the navbar to overlap with some content on the webpage. Page content will adjust to fit the gap left:

Relative positioning places an element relative to the page. Other content on the page, however, does not adjust to fit the gap left:

Sticky positioning, on the other hand, sets the navbar to scroll along with other content until it reaches a specified offset. The page content does not adjust to fit the gap left but scrolls under the positioned navbar:

Update the styling of the .header element as shown below, varying the position property from fixed to relative to sticky, and note how the behavior changes:

.header{
    background-color: var(--black);
    box-shadow: 1px 1px 5px 0px var(--grey);
    position: sticky;
    top: 100px;
    width: 100%;
}

Adding a submenu to the navbar

You can nest menu items inside other menu items. This reduces the amount of space consumed on the navbar and keeps your links organized making it easier for users to explore your site.

To add a submenu, update the index.html file to contain an extra li element as shown below. The fa fa-caret-down icon can be obtained by adding a font-awesome script tag to your HTML file:

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

...
<li class="subnav">
  <p class="subnavbtn">Contact <i class="fa fa-caret-down"></i></p>
  <div class="subnav-content">
      <a href="#">Email</a>
      <a href="#">Twitter</a>
      <a href="#">Phone</a>
  </div>
</li>
...

You can then update your stylesheet to hide and display the submenu when hovered as shown below:

...
/* Sub nav */
.subnav-content {
    background-color:  var(--white);
    width: 100%;
    z-index: 1;
    padding: 20px 0 ;
    display: none;
  }
.subnav-content a {
    color: var(--black);
    text-decoration: none;
    padding: 0;
    margin: 10px 0;
    text-align: center;
}
.subnav:hover .subnav-content {
    display: block;
}
....
@media (min-width: 768px) {
   ....
    /* Sub nav */
    .subnav-content {
        padding: 20px 0 ;
        display: none;
        background-color:  var(--black);
    }
    .subnav-content a {
        color: white;
    }
}

This video demonstrates the app’s submenus:

Horizontal vs vertical mobile navbar

The mobile navigation menu can be displayed horizontally or vertically based on preference. With horizontal navigation, the nav links are placed before the page’s main content. In comparison, with vertical navigation, the links are arranged along the side of the page. Vertical navigation menus are typically opened and closed with a hamburger button.

Pros and cons of the horizontal mobile navbar

The horizontal mobile navbar is the more popular choice, as it’s used on most websites. The left-to-right order feels more natural to English speakers. Additionally, this layout encourages more concise nav link descriptions on the navigation menu.

However, this layout offers limited space for links to be added. As a result, adding top-level links can be challenging, while dropdown menus may overlap web content.

Pros and cons of the vertical mobile navbar

In comparison to the horizontal mobile navbar layout, a vertical mobile navbar provides more room for additional links. As a result, the names of the nav links can be longer, and adding new links is simpler.

However, this flexibility can discourage organized and concise nav links on the navigation menu. Additionally, because this layout is less common and intuitive, users may encounter some difficulty when using the navigation.

Users may also find it difficult to access submenus that extend to the side compared to the horizontal navbar whose submenu extends below.

Compare these vertical and horizontal mobile navbar examples below:

Conclusion

In this tutorial, we designed and built a mobile-first responsive menu using only HTML and CSS, with no JavaScript. The complete code used in this article is available on GitHub on the specific branches.

The technique used in this article is just one of many methods that can be used to build a responsive mobile menu. By experimenting with different methods, you can decide which ones you prefer. Happy coding!

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

Ivy Walobwa Ivy is a Flutter developer and technical writer who is interested in creating awesome developer experiences. She is currently a Program Assistant for the Google Africa Developer Scholarship (GADS), where she helps facilitate the learning of students across Africa.

11 Replies to “Creating a responsive mobile menu with CSS without JavaScript”

  1. I played around with this menu but found that when I scroll down the page and then click on the menu it becomes unstable and drops down the page or even vanishes.

  2. How do I add accessibility and aria attributes to these elements? I’ve spent a lot of time building out this navbar, and found that I ended up needing some JS to do some open/close logic anyway.

  3. maybe I missed it, but how do I remove the scroll from Main content when the menu is open in the mobile version?

  4. when you turn the screen with the menu turned on, it is impossible to click on the last buttons of the list

Leave a Reply