Creating a CSS3 Responsive Menu

Today, I'm going to create a responsive menu using CSS3. This tutorial aims to provide step by step instructions to enable you to create a responsive navigation menu that adapts to varying screen sizes, with the help of CSS media queries.

I used Respond.js and IE HTML5 enabling script so that HTML5 and media queries will work on Firefox 2 and IE. I also used html5doctor.com's Reset Stylesheet for the demo.

Note: The demo works on Chrome, Firefox, Safari and IE8+.

Check out the demo below to see how it works, and feel free to download the example for future use and experiment.

Creating a CSS3 Responsive Menu


Creating a CSS3 Responsive Menu

Design Parameters: work your way up to larger screens

I adopted the Mobile First approach to designing the menu. In essence, this approach adopts a strategy of designing for mobile devices first, then working your way up to larger ones, such as desktop monitors. The base design is developed for the popular dimensions of mobile devices - 320 x 480. I then utilised media queries, primarily for scaling up to larger screen sizes, but also as an effective tool for enhancing the design.

The screen sizes for which the menu is designed are:

  • 320 x 480
  • 480 x 320
  • 768 x 1024
  • 1024 x 768

The Markup

Inside the header I added two nav tags, one for a button which opens the menu, whilst the other contains the menu items. Each menu item contains a with span to represent the icon.

With the objective of hiding the button #menu-button when the screen is wide enough to have the menu constantly visible next to the logo, I placed #menu-drink after #banner-inner-wrapper inside header. This will enable you to situate the menu beside or beneath the logo.

<header id="banner" role="banner">
    <div id="banner-inner-wrapper">
        <div id="banner-inner">
            <hgroup id="title">
                <img id="logo" src="image/logo.png" title="Responsive Bar" />
            </hgroup>
            
            <nav id="menu-nav">
                <!-- button to show and hide the menu -->
                <div id="menu-button">
                    <div id="menu-button-inner"></div>
                </div>
            </nav>
            
        </div>
    </div>
    
    <!-- menu itself -->
    <nav id="menu-drink">
        <ul>
            <li>
                <a class="beer" href="#"><span class="icon"></span>Beer</a>
            </li>
            <li>
                <a class="wine" href="#"><span class="icon"></span>Wines</a>
            </li>
            <li>
                <a class="soft-drink" href="#"><span class="icon"></span>Soft Drinks</a>
            </li>
            <li>
                <a class="coffee-tea" href="#"><span class="icon"></span>Coffee & Tea</a>
            </li>
        </ul>
    </nav>
</header>

CSS Styling

We'll now look at styling this markup with CSS.

I used a CSS table to set the layout of the contents of the header - display:table for #banner-inner-wrapper, display:table-row for #banner-inner and display:table-cell for #title and menu-nav.

Why use a CSS table? Because it's robust against layout changes and it requires less work than floats and display:inline-block if you want to maintain columns side by side.

I also set the logo to scale down to the available space of its container by applying max-width:100%. This prevents the image from staying larger than the width given to its container.

One strategy worth noting here is the use of em instead of px. A useful property of this unit is that it scales nicely to the current font size of the element. To work it out, divide the pixel amount by the current font size (in this case 16px).

#banner-inner-wrapper {
    display: table;
    width: 96%;
    margin: 0.375em auto; /* 6/16 = 0.375em; */
}
#banner-inner {
    display: table-row;
}
#title {
    display: table-cell;
}
#logo {
    max-width: 100%;
    vertical-align: middle;
}
#menu-nav {
    display: table-cell;
    vertical-align: bottom;
    text-align: right;
}

I styled the button that opens up the menu with plenty of CSS3. Specifically I used border-radius, box-shadow and background-image: linear-gradient(...). I also used the transition property to smooth out the transition between various states of the button.

Note that, in the CSS, I excluded browser/vendor specific properties such as -moz-transition and -webkit-box-shadow, for simplicity. In the actual CSS file these properties are included.

#menu-button {
    vertical-align: bottom;
    float: right;
    padding: .375em; /* 6/16 = .375em */
    margin: 0 .375em 0 2em; /* 6/16 = .375em, 32/16 = 2em */
    opacity: .7;
    cursor: pointer;
    
    transition: all .2s linear;
    border-radius: 2px;
    box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
    
    background-color: #4D8EBC;
    background-image: linear-gradient(top,rgba(100, 152, 190, .5),rgba(30, 82, 120, .5));
    background-position: left bottom;
    background-repeat: repeat-x;
}

I altered the style of the button for :active to allow it to give the impression of being selected. Additionally, I added a negative bottom margin to it so that it would sink down, to make it obvious it's been selected.

#menu-button.selected,
#menu-button:active {
    opacity: 1;
    
    box-shadow: 0 1px  1px rgba(120, 120, 120, .2);
    background-color: #2E6288;
    background-image: linear-gradient(top,rgba(25, 68, 99, .5),rgba(30, 82, 120, .5));
    
    margin-bottom: -.375em; /* 6/16 = .375em */
}

I’ve also added a final touch - giving it an image that represents the purpose and the behaviour of the button.

#menu-button-inner {
    background: transparent url(../image/menu-bg.png) no-repeat 0 0;
    width: 78px;
    height: 41px;
}

And here's the outcome of the CSS above:

Buttons

Now let's look up the CSS for the menu itself.

To make sure the menu does not take up the whole screen when it pops up, I considered the following three options:

  1. Option 1: Display all items vertically, each in a separate row
  2. Option 2: Display two items in each row
  3. Option 3: Display all items horizontally, all in one row

Option 1 would be the best choice if the only constraint is the width of the screen, because you don't need to worry about the items being compressed. Unfortunately, however, the height is also very limited, so I elected Option 2.

For the container of the menu #menu-drink, I used box-shadow to make it look like it's slightly floating above the content.

#menu-drink {
    clear: both;
    position: absolute;
    width: 100%;
    background-color: #2e6288;
    
    box-shadow: 0 2px 2px rgba(25, 68, 99, .4);
    
    display: none; /* Not shown initially */
}

To implement Option 2, I set the width of li to 50%.

#menu-drink ul {
    overflow: hidden; /* clears the float */
}
#menu-drink li {
    float: left;
    width: 50%; /* takes up 50% of the available width */
}

The majority of the rest of the CSS for the menu is just for customising the menu items. In the CSS below, I’ve highlighted some important properties that are easy to overlook:

#menu-drink a {
    color: #c2daeb;
    white-space: nowrap;
    text-decoration: none;
    display: block;
    
    transition: color 0.3s linear, background-color 0.3s linear;
    box-shadow: inset 0 0 2px #0A3E64;
    
    padding: .375em; /* 6/16 = .375em */
   
    /* These vertically center the contents of the element */
    line-height: 2.875em; /* 46/16 = 3.375em */
    height: 2.875em; /* 46/16 = 3.375em */
}
#menu-drink a:hover,
#menu-drink a:active {
    color: #fff;
    background-color: #23577D;
}
#menu-drink .icon {
    background: transparent url(../image/drink-flat.png) no-repeat 0 0;
    
    /* This makes the icon to sit next to the text */
    display: inline-block;
    
    vertical-align: bottom;
    opacity: .6;
    width: 2em; /* 32/16 = 2em */
    height: 2.875em; /* 46/16 = 2.875em */
    margin-right: .5em; /* 8/16 = 0.5em */
    
    transition: opacity 0.3s linear;
}
#menu-drink a:hover .icon {
    opacity: 1;
}
#menu-drink .wine .icon {
    background-position: 0 0;
}
#menu-drink .beer .icon {
    background-position: -2em 0; /* 32/16 = 2em */
}
#menu-drink .coffee-tea .icon {
    background-position: -4em 0; /* 64/16 = 4em */
}
#menu-drink .soft-drink .icon {
    background-position: -6em 0; /* 96/16 = 6em */
}

The above mentioned CSS produces the following design, with a screen width of 320px:

320 Menu

This is it for the initial design.

The Media Queries

Noted below are the media queries I used. To keep it short and simple, I've excluded the actual CSS for styling the menu up for different screen sizes. Instead, I’ve inserted a comment to explain what I did for each media query:

@media screen and (min-width: 480px) {
    /* 
     * Made all the items display horizontally as the screen is wide 
     * enough to accommodate it
     */
}
@media screen and (min-width: 560px) {
    /*
     * Made small adjustments to the margin and padding around 
     * the menu items
     */
}
@media screen and (min-width: 768px) {
    /*
     * Made the items display next to the logo. At this width the screen is 
     * large enough so that there is no need to hide the menu
     */
}
@media screen and (min-width: 1024px) {
    /*
     * At this width, everything appears little cramped so I made the 
     * padding around the whole thing larger to give the illusion of space. 
     * I also used larger and cooler icons.
     */
}

You might have noticed the use of min-width: ...px. This can be interpreted as "This media query kicks in when the minimum width is ...px".

jQuery/JavaScript

Phew, we're all done with the CSS! Now we'll look at the jQuery code for the menu. I used jQuery to show or hide the menu when the button is selected, and also when window is resized.

jQuery(function($) {
    // This will hold the state of the pop-up menu
    var open = false;

    function resizeMenu() {
        // If window is less than 480px wide
        if ($(this).width() < 480) {
            if (!open) {
                // Hide the menu if it's not supposed to be displayed
                $("#menu-drink").hide();
            }
            // Display the button
            $("#menu-button").show();
        }
        else if ($(this).width() >= 480) {
            // If window is wider than 480px
            if (!open) {
                // Show the menu if it's not displayed yet
                $("#menu-drink").show();
            }
            // Hide the button if the screen is wide enough for the menu to always show
            $("#menu-button").hide();
        }
    }

    function setupMenuButton() {
        $("#menu-button").click(function(e) {
            e.preventDefault();

            // If already shown...
            if (open) {
                // Hide the menu
                $("#menu-drink").fadeOut();
                $("#menu-button").toggleClass("selected");
            }
            else {
                // If not shown, show the menu
                $("#menu-drink").fadeIn();
                $("#menu-button").toggleClass("selected");
            }
            open = !open;
        });
    }
    
    // Add a handler function for the resize event of window
    $(window).resize(resizeMenu);

    // Initialize the menu and the button
    resizeMenu();
    setupMenuButton();
});

Success! We now have a responsive navigation menu, neatly styled up with CSS3. Check out the demo below, and feel free to download this example for future use.

Conclusion

What do you think of this tutorial. Do you have any other tips on creating a responsive menu with CSS3? Share with us in the comment box below.