A Responsive Drop Down Navigation Menu

Recently I was asked about the responsive menu I created when I redesigned my website. The code for the menu is based on the Suckerfish Dropdowns written on A List Apart years ago. I’ve just restyled parts of it to make it responsive.

Responsive Drop Down Menu in a Browser
Responsive Drop Down Menu in a Browser.

This menu is a slightly improved version of the menu I’m using on my site and it works great in all modern desktop browsers without javascript. In a follow up post I’ll add some javascript to improve the usability of the menu on mobile devices like phones and iPads and also include Superfish. If you’d like to be notified when the follow up post is written subscribe to this site by email. UPDATE: Part 2 has been posted.

The demo uses 320 and up with a single style sheet but could be easily adapted to the HTML5 Boilerplate or pretty much anything.

Check out the demo here. Make sure you re-size your browser.

Download the example here.

The Responsive Menu’s HTML

The HTML is a pretty straight forward HTML5 menu.

<nav id="access" role="navigation">
    <div class="skip-link">
        <a href="#content">Skip to content</a>
    </div>
    <div class="menu">
        <ul class="sf-menu clearfix">
            <li><a href="#" >Menu Item</a></<span class="hiddenGrammarError" pre="li ">li>
            <li</span>>
                <a href="#" >Menu Item</a>
                <ul class="sub-menu">
                    <li><a href="#" >Menu Item</a></<span class="hiddenGrammarError" pre="li ">li>
                    <li</span>>
                        <a href="#" >Menu Item</a>
                        <ul class="sub-menu">
                            <li><a href="#" >Menu Item</a></<span class="hiddenGrammarError" pre="li ">li>
                            <li</span>>
                                <a href="#" >Menu Item</a>
                                <ul class="sub-menu">
                                    <li><a href="#" >Menu Item</a></<span class="hiddenGrammarError" pre="li ">li>
                                    <li</span>><a href="#" >Menu Item</a></li ">li>
                                    <li</span></span></span></span></span></span></span></span></span></span>><a href="#" >Menu Item</a></li>
                                </ul>
                            </li>
                            <li><a href="#" >Menu Item</a></li>
                        </ul>
                    </li>
                    <li>
                        <a href="#" >Menu Item</a>
                        <ul class="sub-menu">
                            <li><a href="#" >Menu Item</a></li>
                            <li><a href="#" >Menu Item</a></li>
                            <li><a href="#" >Menu Item</a></li>
                        </ul>
                    </li>
                </ul>
            </li>
            <li><a href="#" >Menu Item</a></li>
            <li><a href="#" >Menu Item</a></li>
        </ul>
    </div>
</nav><!-- #access -->

Default Style for the Responsive Menu

Defualt Reposive Menu Style

The default style for the menu is what any modern browser on a device with a screen resolution under 600px will see. This is also where you’ll add most of the style that defines the look of the menu. In a responsive design you will include this style in your first style sheet if you’re using multiple style sheets or before any media quires are called if you’re using a single style sheet.

The main difference between the default style of this menu and the Suckerfish menu is that all the main menu items and the drop downs appear vertically rather that horizontal and vertical.

I’ve added more comments to the code than in previous posts, but if you still have questions feel free to ask them in the comments below.

Note: A lot of the CSS that follows could be combined. I haven’t combined it in hopes that it will be easier to read and follow.

/* =Navigation
-------------------------------------------------------------- */
/* clip skip link for screen readers */
.skip-link {
    clip: rect(1px, 1px, 1px, 1px);
    position: absolute !important;
}
#access {
    font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
    overflow:visible;
    z-index:100;
}
/* style the main menu 8*/
.sf-menu{
    border: 1px solid #000;
    border-top: none;
}
/* get rid of padding and margin off all ul's (not sure about using * here, could be better) */
.sf-menu, .sf-menu * {
    margin:0;
    padding:0;
    list-style:none;
}
/* position all dropdowns off screen */
.sf-menu ul {
    position:absolute;
    top:-999em;
}
/* style the main nav list items */
.sf-menu li {
    background: #000;
    background-image: url('../img/menuBg.png'), linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
    background-image: url('../img/menuBg.png'), -o-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
    background-image: url('../img/menuBg.png'), -moz-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
    background-image: url('../img/menuBg.png'), -webkit-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
    background-image: url('../img/menuBg.png'), -ms-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
    background-image: url('../img/menuBg.png'), -webkit-gradient( linear, left bottom, left top, color-stop(0, rgb(36,35,36)), color-stop(1, rgb(0,0,0)));
    background-repeat: repeat-x;
    border-top: 1px solid #242324;
    clear: left;
    float:left;
    position:relative;
    width: 100%;
}
/* change the main nav list items on hover */ 
.sf-menu li:hover {
    background-image: url('../img/menuBg.png'); 
    visibility:inherit; /* fixes IE7 'sticky bug' */
}
/* style all the links */
.sf-menu a {
    font-size: .8em;
    color: #fff;
    display:block;
    padding: 12px 0;
    text-decoration: none;
    text-indent: 12px;
}
/* style the first drop */
.sf-menu li li, .sf-menu li li:hover{
    background: none;
    background-image: none;
}
/* add a larger text indent for the first drop links */
.sf-menu li li a{
    text-indent: 24px;
}
/* add a larger text indent for the second drop links */
.sf-menu li li li a{
    text-indent: 36px;
}
/* add a larger text indent for the third drop links */
.sf-menu li li li li a{
    text-indent: 48px;
}
/* position first drop */
.sf-menu li:hover ul {
    top:auto; /* match top ul list item height */
    position:relative;
}
/* make sure second drop is still off screen */
ul.sf-menu li:hover li ul {
    position: absolute;
    top:-999em;
}
/* position second drop */
ul.sf-menu li li:hover ul {
    top:auto;
    position:relative;
}
/* make sure third drop is still off screen */
ul.sf-menu li li:hover li ul {
    position: absolute;
    top:-999em;
}
/* position third drop */
ul.sf-menu li li li:hover ul {
    top:auto;
    position:relative;
}

The Responsive Menu at Larger Sizes

Responsive Menu at Larger sizes

With most of the style already taken care of this next section gets a bit easier. We’ll give the menu a specific height (you must set a height here so you don’t push the content down when the drop downs appear), override the Clear: left; from the main menu/top level links, give the drop downs a background and reposition them so they appear in the correct locations.

This menu is rather simple, with only 4 links, so I’m adding it to the 600px style sheet. If your menu has more top level links you may want to add this to the 768px style sheet/media query.

@media only screen and (min-width: 600px) {
    /* set height so content isn't pushed down */
    #access{
        float: left;
        height: 36px;
        width: 100%;
    }
    /* set height so content isn't pushed down add z-index to keep drops above content */
    .sf-menu{
        height: 36px;
        z-index: 100;
    }
    /* restyle so main links are horizontally aligned */
    .sf-menu li {
        clear: none;
        width: 25%; /* this will need to be adjusted for your needs */
    }
    /* new style for drop list items */
    .sf-menu li li{
        background: #000;
        background-image: url('../img/menuBg.png'), linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
        background-image: url('../img/menuBg.png'), -o-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
        background-image: url('../img/menuBg.png'), -moz-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
        background-image: url('../img/menuBg.png'), -webkit-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
        background-image: url('../img/menuBg.png'), -ms-linear-gradient(bottom, rgb(36,35,36) 0%, rgb(0,0,0) 100%);
        background-image: url('../img/menuBg.png'), -webkit-gradient( linear, left bottom, left top, color-stop(0, rgb(36,35,36)), color-stop(1, rgb(0,0,0)));
        background-repeat: repeat-x;
        clear: left;
        width: 100%;
    }
    /* reset text indent on all drop a tags and set the width to 100% */
    .sf-menu li li a, .sf-menu li li li a, .sf-menu li li li li a{
        text-indent: 12px;
        width: 100%;
    }
    /* reposision and style the first drop */
    .sf-menu li:hover ul{
        background: #000;
        left: auto;
        position: absolute;
        top: -1;
        width: 100%;
        z-index: 100;
    }
    /* reposision and style the second drop */
    ul.sf-menu li li:hover ul{
        background: #000;
        position: absolute;
        top: -1px;
        left:100%;
    }
    /* reposision and style the third drop */
    ul.sf-menu li li li:hover ul{
        background: #000;
        position: absolute;
        top: -1px;
        left:100%;
    }
}

The Responsive Menu at Even Larger Sizes

This next section doesn’t do much. I’m basicaaly just increasing the size of the fonts and height of the menu to for bigger browsers, because lets face it, size does matter!

@media only screen and (min-width: 768px) {
    header h1{
    font-size: 30px;
}
}
@media only screen and (min-width: 992px) {
    header h1{
        font-size: 36px;
    }
    /* bigger screen bigger nav */
    #access{
        height: 50px;
    }
    /* bigger screen bigger nav */
    .sf-menu{
        height: 50px;
    }
    /* bigger screen bigger font size and padding */
    .sf-menu a {
        font-size: 1em;
        padding: 17px 0;
    }
}

An Even Bigger Website with a Responsive Menu

An Even Bigger Website with a Responsive Menu

Here’s where this demo gets an improvement over my site. I’d encourage you to look at the browser statistics of your own site to see if you should actually spend time developing for screen resolutions larger than 1382px. If the answer is yes, than read on.

Again, with most of the heavy lifting already taken care of up to this point, the following style’s pretty much a breeze.

First we’ll resize the header, float the header and the content to the left, clear the top level links again and set all drop downs hovers to left 100%;

@media only screen and (min-width: 1382px) {
    /* move the header to the left side of the screen */
    header{
        float: left;
        margin: 0 2% 0 0;
        width: 20%;
    }
    header h1{
        font-size: 45px
    }
    .content{
        float: left;
        width: 78%;
    }
    #access, .sf-menu{
        height: auto;
    }
    .sf-menu li {
        clear: left;
        width: 100%;
    }
    /* reposition the first drop */
    .sf-menu li:hover ul{
        left: 100%;
        top: -1px;
        position: absolute;
    }
 
}

Conclusion

There you have it, a responsive menu in about 200 lines of CSS that works great without javascript. If you have any questions feel free to leave a comment below.

Check out the demo here. Make sure you re-size your browser.

Download the example here.

Read Part 2 in this series here.

18 Comments

  1. Jayne
    Posted July 2, 2012 at 5:22 pm | Permalink

    I was looking at the demo, thinking this is great. Then I realized that when the screen is less than 600 pixels, it’s impossible to get to some of the menu items. I added some numbers to your code so I could explain the problem.

    Main Menu Item 1

    Main Menu Item 2

    Menu Item 2.1

    Menu Item 2.2

    Menu Item 2.2.1

    Menu Item 2.2.2

    Menu Item 2.2.2.1
    Menu Item 2.2.2.2
    Menu Item 2.2.2.3

    Menu Item 2.2.3

    Menu Item 2.3

    Menu Item 2.3.1
    Menu Item 2.3.2
    Menu Item 2.3.3

    Main Menu Item 3
    Main Menu Item 4

    If you open the menu so you can see Menu Items 2.2.2.1, 2.2.2.2, 2.2.2.3, and 2.2.2.4 and then touch the link to either Menu Item 2.2.3 or 2.3 the menu reverts to its original state displaying Menu Items 1, 2, 3, and 4. Is that because it is crossing over a tag or is it something else.

    • Posted July 3, 2012 at 8:35 am | Permalink

      Hi Jayne,
      The menu in it’s current state responds to the hover event. When you mouseover/hover over a list item with a drop down it shows the menu. When you mouseout/hover out it hides the drop down. In the next part of this series I’ll address this problem on mobile devices and add superfish for desktop browsers which will take care of these problems.

  2. Tim Jones
    Posted July 3, 2012 at 7:47 am | Permalink

    Great tutorial, How did you do the navigation that you have on your site now? Where it replaces the navigation with a button. I’m assuming there some javascript involved. Would you be willing to do a tutorial on that?

    Here’s what I have so far. I’ve only styled for the screen and phone sizes, not the tablet. Still working on the phone size, mainly the navigation is the issue.

    • Posted July 3, 2012 at 8:45 am | Permalink

      Hi Tim,
      I’m just using jQuery to hide and show the menu. In the next part of this series I’ll show you how to do this.

  3. ravi
    Posted July 18, 2012 at 4:19 am | Permalink

    It is not working in internet explorer

    • Posted July 18, 2012 at 8:48 am | Permalink

      It should work fine in IE9. You would want to add specific style for older versions of IE or use modernizr to test for media quires and load in respond.js

      • ravi
        Posted July 18, 2012 at 9:59 pm | Permalink

        Thanks for your reply,

        I am working with IE8. by the way it is awesome
        tutorial works with all browsers except f**king IE

  4. Posted July 29, 2012 at 8:22 pm | Permalink

    Part 2 of this series has been added. Read it here.

  5. Helen
    Posted October 24, 2012 at 4:53 pm | Permalink

    Hello Eric,

    Thank you so much for this, it is great. I am trying to adopt it to a design, but the problem is that I have more than 4 items in the main navigation menu. When the window is at it’s largest, I’d like all 7 of my menu items to line up in one row (there is plenty of space), instead of jumping back to a 4 row align left menu bar, or lining up in 2 as it happens in the middle sizing.

    I’ve tried a whole bunch of things, but I can not for the life of me figure out how to have all the 7 menu items in one line.

    Would you be able to give me a clue?

    Many thanks,
    Helen

    • Posted October 25, 2012 at 9:00 am | Permalink

      Hi Helen,
      The list items are currently set to 25% each when the menu is displayed horizontally. If you want to add more menu items you need to remove that or adjust is so it works with the number of items you add.
      Line 495 in the css of the demo.

      .sf-menu li {
      clear: none;
      width: 25%;
      }

      Hope that helps.

  6. Todd
    Posted January 3, 2013 at 1:17 am | Permalink

    Very nice. But I’d love to see a version where the width of the drop-downs is not limited to the width of the top-tabs. That would allow this to be used for a main menu with 7 or 8 tabs, instead of just 4 or 5. I think it’s pretty common for the labels in the drop-downs (which are stacked vertically) to be longer than those in the top-tabs (horizontal).

    • Posted January 9, 2013 at 8:30 am | Permalink

      Hi Todd,
      You can have as may “top-tabs” as you’d like. You just need to adjust the width of them. Right now they are set to 25% because there are only 4. You can also adjust the width of the drop-down to anything that you would like. It’s set to 100% in the example but could be changed to pixels. Line 519 in the CSS.

      • Stanislas Rolland
        Posted May 30, 2013 at 2:08 pm | Permalink

        Hi Eric,

        Many thanks for this nice tutorial.

        You wrote: “You can also adjust the width of the drop-down to anything that you would like. It’s set to 100% in the example but could be changed to pixels. Line 519 in the CSS.”

        I did that because I wanted to have the horizontal items sized to their content, which looks more natural to me. It worked fine until I added all the JavaScript of the second part. Then, in the desktop context, the items of the first level appear to be automatically resized to the width of the dropdown. How could I avoid this? Is this due to the superfish script?

        Thanks again,
        Stanislas

  7. Posted January 24, 2013 at 1:29 pm | Permalink

    Hi Eric! Thanks for sharing this…it is tremendously helpful. I’ve been trying to convert my own HTML4.01/CSS menu to this structure/solution for the past several days. It’s working great! Except for some z-index issues on iPhones and iPads (menu being hidden behind other page elements…where it is correctly sitting at the “top” of the page on Chrome, Firefox (Mac and PC), etc.

    Any ideas as to why z-index wouldn’t be displaying properly in iPhones and iPads, specifically? An example of that problem can be found at: http://www.mapformation.com/portfolio/campus/3Dvectornew.htm and/or http://www.mapformation.com/aboutus/clients/mashupnew.htm

    Oh yes, one other problem (as I just shared that last link): I’ve noticed the “pinch” and click/drag functions inside of a few of my Google mashups are also misbehaving inside your new responsive/menu code. Any ideas why that might be occurring, or is that something I should be asking on the Google boards?

    Thanks again! I really appreciate this. – Derek

    • Posted March 23, 2013 at 3:02 pm | Permalink

      Hi Derek,
      I would try adding a z-index:100 on the header element and not just the nav. I’m not sure about the problem you’re having with google maps. I haven’t played around with them much.

  8. Macce
    Posted October 20, 2013 at 2:56 pm | Permalink

    Works pretty well in most environments. However, WP8 (Nokia Lumia) refuses to show the submenus. The same goes for 80% of other similar approaches so there must be a “pattern” everybody is using (and not testing the functionality in Windows Phone).

    Yes, it is a marginal environment but cannot be omitted.

  9. Posted January 22, 2014 at 10:53 am | Permalink

    Thanks for you code !
    I am doing a new website in Dreamweaver CC, using Grid Layout Table, and I don’t know how can I oblige the dropmenu overlay the element bellow … it resizes the navigation element (moving all content down) when a menu item is selected.
    Do you know how can I solve this problem ?
    Best Regards, LL

  10. Posted October 12, 2014 at 12:25 pm | Permalink

    Thanks Eric.

    I know you posted this long time ago, but in the last couple of weeks I spent/wasted a lot of time trying to make the Whiteboard’s (framework for WordPress) menu responsive. This is the only solution that worked.

    I owe you a beer/tea.

    Best,
    Marius

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>