Bulletproof CSS3 Dropdown Navigation Menu

Bulletproof CSS3 Dropdown Navigation Menu

This entry was published on January 26, 2012 and may be out of date.

One of the most critical aspects of web design is the navigation. The navigation, above all else, must be clear, usable and cross-browser compliant. But the tragedy is in the fact that most websites have awful navigation designs that make content difficult to navigate to.

In this tutorial, we are going to build a fabulous cross-browser dropdown navigation menu with the help of some advanced CSS3 properties. CSS has made leaps and bounds in recent years. With an ever-solidifying specification and widespread browser support, using CSS3 properties today is very much a feasible option.

The world has moved on quite a bit and these days supporting our beloved browser Internet Explorer 6 is no longer a business requirement for most of our clients. Therefore, we can start taking advantage of the new technologies and reap their benefits.

Although CSS3 support still varies from browser to browser as they rely on proprietary vendor-specific properties. We will have a deep look into these properties as this tutorial progresses.

The demo

Before we go into the nitty-gritty of front-end web development, let us have a look at what we are trying to accomplish. Without further ado, the fully functional demo is presented below:

See the Pen Step 7: Bulletproof CSS3 Dropdown Navigation Menu by Saddam Azad (@azadcreative) on CodePen.20544

Such navigation menus are common around the web. But here, in this tutorial, we are going to build it with CSS3 techniques without using a single image. We will also provide a fallback for older non-supporting browsers – thus achieving maximum cross-browser compatibility.

The markup

We are going to start from the basic top-level navigation only. After we have successfully styled the top-level horizontal navigation, we will gradually move to the second and third levels. The top-level navigation is simply an unordered list wrapped around a HTML5 <nav> tag.

<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Breakfast</a></li>
<li><a href="#">Brunch</a></li>
<li><a href="#">Lunch</a></li>
<li><a href="#">Tea</a></li>
<li><a href="#">Supper</a></li>
</ul>
</nav>

Internet Explorer versions 8 and earlier cannot process HTML5 elements, let alone style them. Therefore, you must include the Modernizr script in your pages. Modernizr is also handy for the purpose of progressive enhancement.

The basic CSS scaffolding

The scaffolding includes Eric Meyer’s CSS Reset and some basic styling that manipulates the markup to make a basic navigation. Let’s have a closer look:

Firstly, we are going to design a horizontal navigation menu as seen at the top of this website. In the following CSS code, we are going to define the structures of the document and the navigation menu.

body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 30px;
font-weight: 300;
background: url(http://i65.tinypic.com/2ccuoh2.jpg);
min-width: 600px;
}
section.content {
width: 660px;
min-width: 500px;
margin: 0 auto;
text-align: center;
zoom: 1;
}
section.content:before,
section.content:after {
display: table;
content: "";
zoom: 1;
}
section.content:after {
clear: both;
}
header {
padding: 30px 0 0 0;
text-align: center;
}

The CSS code here is pretty much self-explanatory but allow me to explain certain bits and pieces:

  • On the body element, we are applying a background image and setting the typography and layout settings for the entire document.
  • You are probably wondering what those :before and :after pseudo-classes are for. Maybe puzzled over the display:table and content: “” too? This is Nicolas Gallagher’s awesome micro clearfix hack.
nav {
zoom: 1;
margin: 20px auto 0 auto;
width: auto;
text-align: left;
background-color: #efefef;
}
nav:before, nav:after {
display: table;
content: "";
zoom: 1;
}
nav:after {
clear: both;
}

nav ul {
float: left;
zoom: 1;
width: auto;
background: #FFF;
z-index: 100;
border-right: 1px solid #e8e8e8;
}
nav ul:before, nav ul:after {
display: table;
content: "";
zoom: 1;
}
nav ul:after {
clear: both;
}

The HTML5 nav element and the ul elements both gets a bit of styling and the same clearfix technique as before.

nav ul li {
float: left;
padding: 0 0 10px 0;
position: relative;
outline: none;
line-height: 1.2em;
padding: 0 1px 0 0;
border-left: 1px solid #e8e8e8;
}
nav ul a {
padding: 15px 20px 15px 20px;
font-weight: bold;
outline: none;
float: left;
display: block;
zoom: 1;
}
nav ul a:link, nav ul a:visited {
color: #666;
text-decoration: none;
background-color: #ededed;
background-repeat: repeat-x;
outline: none;
}
nav ul a:hover, nav ul a:active {
color: #a67b45;
text-decoration: none;
background-color: #ffffff;
}

Finally, here is what happens:

  • The list items are floated left to make a horizontal navigation menu.
  • The anchors are also floated left and styled with colour and background colour.

At this point your document should look something like this:

See the Pen ojyeme by Saddam Azad (@azadcreative) on CodePen.20544

Now let’s get to the good stuff!

CSS3 Gradient

Before we go further, be sure to have read Chris Coyier’s excellent article on CSS3 Gradients if you have not already.

CSS3 gradients are incredibly useful for designing modern websites. Apart from providing flexibility in implementing design, they also reduce the number of HTTP requests — thus improving page load times.

nav {
...

background-repeat: repeat-x;
background-image: -moz-linear-gradient(top, #ffffff, #ededed); /* FF3.6+ */
background-image: -ms-linear-gradient(top, #ffffff, #ededed); /* IE10 */
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #ededed)); /* Safari 4+, Chrome 2+ */
background-image: -webkit-linear-gradient(top, #ffffff, #ededed); /* Safari 5.1+, Chrome 10+ */
background-image: -o-linear-gradient(top, #ffffff, #ededed); /* Opera 11.10+ */
background-image: linear-gradient(top, #ffffff, #ededed); /* The standard */
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed', GradientType=0); /* IE9 and below */
}

Firstly, we are adding CSS Gradients on the nav element (the code is highlighted in bold text). It is a subtle white-to-grey gradient applied using background-image property. Browsers that do not support CSS3 Gradients will fall back to the design displayed earlier.

You may have noticed that there are vendor prefixes for different browsers. Here is the order I use:

  1. Firstly -moz-linear-gradient for older versions of Firefox
  2. Followed by -ms-linear-gradient for the upcoming version of Internet Explorer, version 10 (at the time of writing)
  3. -webkit-gradient for older Webkit based browsers such as Safari 4 and Chrome 2.
  4. For newer versions of Webkit browsers, apply -webkit-linear-gradient
  5. For Opera versions 11.10 and above, -o-linear-gradient
  6. The official standard for CSS3 Gradients: linear-gradient
  7. Finally, everyone’s favourite browser family (Internet Explorer) needs a proprietary filter property, which is not valid CSS.

Exhausted? Well, this is the best we can do for now. In the future when everybody has a browser that supports CSS3 Gradients according to the official specification, simply remove all vendor-specific code.
Now let us apply the same gradient to the anchors.

nav ul a:link, nav ul a:visited {
...

background-image: -moz-linear-gradient(top, #ffffff, #ededed);
background-image: -ms-linear-gradient(top, #ffffff, #ededed);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #ededed));
background-image: -webkit-linear-gradient(top, #ffffff, #ededed);
background-image: -o-linear-gradient(top, #ffffff, #ededed);
background-image: linear-gradient(top, #ffffff, #ededed);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed', GradientType=0);
}
nav ul a:hover, nav ul a:active {
...

background-repeat: repeat-x;
background-image: -moz-linear-gradient(top, #ededed, #ffffff);
background-image: -ms-linear-gradient(top, #ededed, #ffffff);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ededed), color-stop(100%, #ffffff));
background-image: -webkit-linear-gradient(top, #ededed, #ffffff);
background-image: -o-linear-gradient(top, #ededed, #ffffff);
background-image: linear-gradient(top, #ededed, #ffffff);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff', GradientType=0);
}

The highlighted code above adds a CSS3 gradient on each of the anchors within the navigation menu. For hover and active states, the gradient has been reversed.

Here is what your document should look like now:

See the Pen ojMYQy by Saddam Azad (@azadcreative) on CodePen.20544

CSS3 Box Shadows

Now that the CSS3 Gradients have been applied, things are looking good now. We are going to apply some CSS3 Box Shadows on the navigation menu to add some visual depth to the design.

nav {
...

-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

We are going to add some nifty inset CSS Box Shadows on the :active pseudo-classes of the anchors. That way when you click on the navigation items, you get a more interactive experience.

nav ul a:active {
-webkit-box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3) inset;
-moz-box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3) inset;
box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.3) inset;
}

As demonstrated below, there are multiple applications of CSS Box Shadows:

See the Pen bVjBZN by Saddam Azad (@azadcreative) on CodePen.20544

CSS3 Transition

CSS3 provides us the ability to include transition effects that were only once available on Adobe Flash1. Transitions are meant to be used sensibly to enrich the user experience. In this case, I am attempting to create a desktop-application-esque experience for the users.

The transition is to be placed so that the box shadows and colour of anchors change with a subtle transition effect. It is barely noticeable but adds to the overall smoothness of the interface.

nav ul a:link, nav ul a:visited {
...

-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
-ms-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
}

nav ul a:hover, nav ul a:active {
...

-webkit-transition: all 0.1s ease;
-moz-transition: all 0.1s ease;
-ms-transition: all 0.1s ease;
-o-transition: all 0.1s ease;
transition: all 0.1s ease;
}

The transition is set to all because we are transitioning two different properties of the anchor: the link colour and box-shadow. We could use transition: colour and transition: box-shadow separately but that would add too much redundancy with all the vendor-specific code.

If you are curious about the syntax of the transition, please refer to Dan Cederholm’s excellent guide to CSS3 Transitions on A List Apart.

Let’s have a closer look at the vendor specific codes:

  1. We start with Webkit based browsers
  2. Followed by Mozilla
  3. The upcoming Microsoft Internet Explorer 10 comes with full support for CSS3 Transitions. Versions 9 and below have no support whatsoever.
  4. Opera version 10.5+ supports transitions
  5. Finally, the CSS3 standard.

The document should now resemble something like this:

See the Pen ojMBga by Saddam Azad (@azadcreative) on CodePen.20544

The dropdown markup

<ul id="menu">
<li><a href="#">Home</a></li>
<li class="breakfast active"><a href="#">Breakfast</a>
<ul>
<li class="bread"><a href="#">Bread</a>
<ul>
<li class="french"><a href="#">French</a></li>
<li class="english"><a href="#">White</a></li>
<li class="brown"><a href="#">Brown</a></li>
</ul>
</li>
<li class="eggs"><a href="#">Eggs</a>
<ul>
<li><a href="#">Eggs Benedict</a></li>
<li><a href="#">Deviled Eggs</a></li>
<li><a href="#">Scotch Eggs</a></li>
<li><a href="#">Scrambled Eggs</a></li>
</ul>
</li>
<li class="bacon"><a href="#">Bacon</a>
<ul>
<li><a href="#">Canadian</a></li>
<li><a href="#">Southern</a></li>
<li><a href="#">Crisp</a></li>
</ul>
</li>
<li class="jam"><a href="#">Preserves</a>
<ul>
<li><a href="#">Raspberry</a></li>
<li><a href="#">Gooseberry</a></li>
<li><a href="#">Strawberry</a></li>
<li><a href="#">Blackberry</a></li>
</ul>
</li>
<li class="coffee"><a href="#">Coffee</a>
<ul>
<li><a href="#">Cappuccino</a></li>
<li><a href="#">Latte</a></li>
<li><a href="#">Espresso</a></li>
<li><a href="#">Macchiato</a></li>
</ul>
</li>
</ul>
</li>

...
</ul>

The code above is simply a sample of the full markup. As you can see, there are nested unordered lists within the top-level list items.

Now that we have a complete nested HTML structure in place, it is time to style them.

Styling older browsers first

The principles of progressive enhancement teaches that we must cater for older browsers first before adding more features to newer, more standards-compliant browsers. Following those principles, we will be styling a basic version of the dropdown navigation first.

#menu li:hover > ul {
display: block;
}

#menu li:hover > a {
color: #a67b45;
}

nav li ul {
display: none;
margin: 0;
position: absolute;
top: 46px;
left: 0px;
width: 130px;
border: none !important;
padding: 2px;
background: #999;
z-index: 1000;
zoom: 1;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}

nav li ul:before, nav li ul:after {
display: table;
content: "";
zoom: 1;
}

nav li ul:after {
clear: both;
}

nav li ul a {
float: none;
}

nav li ul li {
width: 130px;
display: block;
border-top: 1px solid #cfcfcf;
border-left: none !important;
}

nav li ul li ul {
display: none;
left: 130px !important;
top: -3px;
}

At the beginning of the code block, you will see that when you hover on the top-level item, its children are displayed by switching its display property to block.

Then the code goes on to define the structure of the sub-level menu system. Some recurring features such as the clearfix hack and CSS3 Box Shadows are seen at play.

What you will across all browsers at this point is this:

See the Pen Step 5: Bulletproof CSS3 Dropdown Navigation Menu by Saddam Azad (@azadcreative) on CodePen.20544

Progressively enhanced

Modernizr adds classes to the html element of the document according to the capabilities available in the browser. We will require browsers that support RGBA to display an enhanced design.

.rgba nav li > ul {
padding: 10px;
left: -10px;
background: rgba(0, 0, 0, 0.1);
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}

.rgba nav li ul li ul {
top: -10px;
}

.rgba nav li > ul {
margin-top: -1px;
}

Modernizr adds the class rgba to the html element of the document when the document loads. Using this method, we can style advanced browsers without affecting the older non-compliant ones.

With the enhanced design, standards-compliant browsers should now display the navigation with all the CSS3 RGBA goodness.

See the Pen Step 6: Bulletproof CSS3 Dropdown Navigation Menu by Saddam Azad (@azadcreative) on CodePen.20544

The jQuery plugin

For the purpose of this navigation, we are going to build a custom jQuery plugin. The purpose of the plugin is to:

  1. Find out if the navigation items have any children. If they do, add a hasChildren classname
  2. Wrap the inside list items anchors with a span, which is useful for applying styling
  3. Apply the class hover when an onHover event occurs on the list items

Allow me to give to give you the code straight away:

$.fn.nav_dropdown = function(options) {

var defaults = {};
var opts = $.extend(defaults, options);

// Apply on those items with children
this.each(function() {
$(this).find('li').each(function() {
if($(this).find("ul").length > 0) {
$(this).addClass("hasChildren");
$(this).find("> a').wrapInner("<span></span>");
}
});
});

// Apply on all list items
$(this).find("li").hover(function() {
$(this).addClass('hover');
}, function() {
$(this).removeClass('hover');
});

$('li').has('ul').hover(function(){
$(this).children('ul').show();
}, function() {
$(this).children('ul').hide();
});
};

As you can see above, the jQuery plugin is very basic and nothing to be overwhelmed with. I am using the wonderful jQuery plugin authoring boilerplate provided by Mike Alsup of Learning jQuery.

Now all you have to do is to call the function and tell it what HTML element to apply it to:

$(function() {
$('nav').nav_dropdown();
});

We have added the hasChildren class to the list items that contain further items as children. We will have to provide additional styling, so that users can visually tell them apart from the childless items.

Styling items with children

Before we go into the CSS styling, we have to create a CSS Sprite with arrows that act as pointers. There are four elements within the sprite as demonstrated below.

The CSS Image Sprite

Now that we have an image with arrows, let us apply the styling. Firstly, the top-level items get their visual cues with the following code:

nav ul li.hasChildren a span {
padding: 0 20px 0 0;
background: url(http://i63.tinypic.com/2ufwmcg.png);
background-position: right top;
background-repeat: no-repeat;
display: block;
}
nav ul li.hasChildren a:hover span, nav ul li.hasChildren.hover a span {
background-position: right -50px;
}

Now for the sub-level navigation items, we are going to apply some styling:

nav li ul li.hasChildren a > span {
background: url(http://i63.tinypic.com/2ufwmcg.png);
background-position: right -97px !important;
background-repeat: no-repeat;
}
nav li ul li.hasChildren a:hover > span,
nav li ul li.hasChildren.hover > a span {
background-position: right -138px !important;
}

With the help of a little javascript and an image sprite, we have created a navigation where items are differentiable according to whether they have children.

And viola! That is all there is. Here is the final demo, one more time:

See the Pen Step 7: Bulletproof CSS3 Dropdown Navigation Menu by Saddam Azad (@azadcreative) on CodePen.20544

Conclusion

Congratulations, you now have a completely cross-browser compliant CSS3 Navigation Menu that is fully functional in older non-supporting browsers and takes advantage of advanced CSS3 features in modern browsers.

If this tutorial has been helpful to you or If you happen to be a front-end development guru who knows more than me, your feedback would be very much appreciated.

Further reading

For further reading, here are some excellent resources from world-class web developers:

Please use this code for learning purposes only and please do not blindly copy/paste your way to glory. I cannot guarantee that the code will not break your existing layout. I herewith declare myself not responsible if the code presented here happens to break layouts or kill your neighbour’s cat.

As demonstrated above, we have styled a simple unordered list to design an outstanding cross-browser compliant navigation menu.

  1. Be sure to read Rich Bradshaw’s comprehensive guide to CSS3 Transitions, Transforms and Animation. []