Create a responsive mobile menu with CSS

A navigation menu that is clear, concise, and easy to intuitively navigate is essential for an optimized website user experience. The responsiveness of the menu is a key factor as well. As of this writing, more than 54 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.

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.

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">
    <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="">
    <!-- 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>
    <!-- Main content -->
                Some content
                More Content

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("[email protected];700&display=swap"); /* import font */

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

/* Reset */
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    background-color: var(--white);
    font-family: "Poppins", sans-serif;
    text-decoration: none;
    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

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

This code 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.

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

/* Nav menu */
    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);
    max-height: 0;
    transition: max-height .5s ease-out;

In this code, 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.

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

CSS Styled Navigation Menu

/* Menu Icon */
    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 */

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

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

In this code, 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 to hide the checkbox (.side-menu).

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

CSS Styled Hamburger Menu

/* 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);
.side-menu:checked ~ .hamb .hamb-line::after {
    transform: rotate(45deg);

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.

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) {
        max-height: none;
        top: 0;
        position: relative;
        float: right;
        width: fit-content;
    .menu li{
        float: left;
    .menu a:hover{
        background-color: transparent;
        color: var(--gray);


        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:


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.

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.

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. 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 apps — .

Categorized as SEO

By Nguyen Manh Cuong

Nguyen Manh Cuong is the author and founder of the nguyendiep blog. With over 14 years of experience in Online Marketing, he now runs a number of successful websites, and occasionally shares his experience & knowledge on this blog.

Leave a comment

Your email address will not be published. Required fields are marked *