Code a Responsive Navigation Menu

Navigation menus used to be a fairly simple thing. Code up an unordered list, float it left and you’re good to go. With responsive design being all the rage these days though you’re faced with some new challenges when creating a menu design.

Follow along as we start from scratch and code a simple but effective responsive navigation menu that you can easily modify and reuse in your own projects.

What We’re Building

If you’re the kind of person who likes to skip ahead, here’s a sneak peek at what we’re building. Be sure to resize your browser window to see the responsive concepts in action.

Demo: Click here to see and tweak it on Dabblet.

screenshot

The HTML

Let’s jump right into this project without a bunch of unnecessary fluff. I’m sure you’re eager to get to the good stuff.

The first step is to decide on some markup. Since this is a navigation menu, it’s the perfect place to implement the HTML5 “nav” element.

<nav>
</nav>

Believe it or not, this one little piece of code had my head spinning when it came time to test. For some reason unknown to me, my styles simply weren’t taking effect in IE6-8! After a good ten minutes of fumbling around, I slapped myself in the head as I realized that I had used HTML5 elements, which of course aren’t supported by anything older than IE9.

Fortunately, the fix is easy, just drop in the famous html5shiv and you’re good to go (place this in the head portion of your document).

<!--&#91;if lt IE 9&#93;>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<!&#91;endif&#93;-->

Add the List

Now that we’ve got our container element figured out, it’s time to toss in a simple UL with eight links. This tutorial is pretty dependent on the number of links so be sure to use eight if you’re not confident enough to venture out on your own in some areas.

<nav>
   <ul>
      <li><a href="#">PixelsDaily</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Clients</a></li>
      <li><a href="#">Work</a></li>
      <li><a href="#">Podcast</a></li>
      <li><a href="#">Downloads</a></li>
      <li><a href="#">Blog</a></li>
      <li><a href="#">Contact</a></li>
   </ul>
</nav>

Add The Sub Tag

To make the menu a little more interesting and helpful, let’s include another line of text below the initial one on each of the menu items. There are a number of ways that we could go about this so feel free to disagree with how I approached it. Basically I just tossed in a break tag and used a small element for the second line. The second item will indeed be small so this gives us a fairly semantic hook without any unnecessary divs, IDs or classes.

<nav>
   <ul>
      <li><a href="#">PixelsDaily<br /> <small>Go Home</small></a></li>
      <li><a href="#">About<br /> <small>Meet Us</small></a></li>
      <li><a href="#">Clients<br /> <small>Meet Our Friends</small></a></li>
      <li><a href="#">Work<br /> <small>See Our Work</small></a></li>
      <li><a href="#">Podcast<br /> <small>Hear Us</small></a></li>
      <li><a href="#">Downloads<br /> <small>Steal Our Stuff</small></a></li>
      <li><a href="#">Blog<br /> <small>Read About Us</small></a></li>
      <li><a href="#">Contact<br /> <small>Email Us</small></a></li>
   </ul>
</nav>

Progress Check

After this step you should have a simple, unstyled list of links. Notice that without any effort, those small tags are already going to work and reducing the size of every other line.

screenshot

Starter Styles

To begin our CSS, let’s pull out that good old universal selector and take Paul Irish’s advice by applying box-sizing: border-box to every element. This will help us size things easily with percentages even though we’ll be using borders.

* {
	padding: 0;
	margin: 0;
	
	-webkit-box-sizing: border-box;
	   -moz-box-sizing: border-box;
	        box-sizing: border-box;
}

Container Styles

Before we get into styling the individual list elements, let’s apply some basic styling to our parent elements. First up, target the nav element and set it to 90% of the width of the body. This will stretch most of the way across the available space but still leave our nav a little room to breathe. Be sure to center it on the page as well with auto margins.

Also jump in and apply the necessary unordered list styles to reset the list and eliminate any bullets.

nav {
	width: 90%;
	margin: 50px auto;	
}

nav ul {
	list-style: none;
	overflow: hidden;
}

screenshot

Menu Styles

Now that our parent elements have their act together, it’s time to work specifically on the menu items, which can be targeted via the nav element in conjunction with the anchor tags and list items.

Here’s a big chunk of code but don’t be afraid of it, I’ll walk you through it piece by piece.

nav li a {
	background: #444;
	border-right: 1px solid #fff;
	color: #fff;
	display: block;
	float: left;
	font: 400 13px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	padding: 10px;
	text-align: center;
	text-decoration: none;
	text-transform: uppercase;
	width: 12.5%;
}

/*SMALL*/
nav small {
	color: #aaa;	
	font: 100 11px/1 Helvetica, Verdana, Arial, sans-serif;
	text-transform: none;
	
}

I’ve ordered the properties here alphabetically for easy reference but to be honest this isn’t really my preferred way to do things. I like to group styles by function so that they make a little more sense. If we reorder our styles this way, they become easier to discuss.

nav li a {
	display: block;
	float: left;
	width: 12.5%;
	padding: 10px;
	
	background: #444;
	border-right: 1px solid #fff;
	
	color: #fff;	
	font: 400 13px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	text-align: center;
	text-decoration: none;
	text-transform: uppercase;
}

First up is the chunk of styles that defines the shape and layout of each list item. We’ve changed the anchors to block level items, floated them to the left, assigned a width and given them a little padding. This will create a nice big rectangle for each link in the menu. Why 12.5% for the width? Because we have eight menu items and 100% / 8 gives us 12.5% width on each item.

Next up is the chunk of code that defines the visual styling of the boxes that we just created. We’ve given them a background color and a single pixel border on the right side.

Finally, we end with all of the styles that relate to the text of the menu. This is all pretty basic stuff, the one curve ball is the “Cutive” font, which you can find here.

For the small styles, I changed the color, removed the uppercase text-transformation and set the font to Helvetica.

Progress Check

These styles made all the difference in the world. Our menu is looking much better. The custom font is working, the links are inline, the small text is perfect; it’s a beautiful thing.

screenshot

Hover Styles

When the user hovers over a menu item, let’s take it all the way to black. To make it feel a little nicer, I added a half second transition as well.

nav li a {
	background: #444;
	border-right: 1px solid #fff;
	color: #fff;
	display: block;
	float: left;
	font: 400 13px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	padding: 10px;
	text-align: center;
	text-decoration: none;
	text-transform: uppercase;
	width: 12.5%;
	
	/*TRANSISTIONS*/
	-webkit-transition: background 0.5s ease;
	   -moz-transition: background 0.5s ease;
	     -o-transition: background 0.5s ease;
	    -ms-transition: background 0.5s ease;
	        transition: background 0.5s ease;
}

/*HOVER*/
nav li a:hover {
	background: #222;
}

screenshot

Border Fix

Now, you can’t see it given that our background is white, but there’s actually a problem with our borders. They’re really just meant to give a little bit of visual definition to each menu item, which means there’s no need for one at the very end. To fix this, we can use the last:child pseudo class.

nav li:last-child a {
	border: none;
}

Below I’ve applied a temporary background color just so you can see the difference between the before and after versions.

screenshot

Going Responsive

This project serves as a good example of the difference between a responsive design and a fluid one. At this point, our menu is fluid (it uses percentage-based widths) but it isn’t responsive. We can see this clearly when we attempt to reduce the size of our window too much:

screenshot

As you can see, this is a wreck! Let’s jump in with some media queries to see if we can fix this problem. The best way to find out where we need media queries is simply to test it out and see where the layout breaks, then account for it.

1220px

The first problem that I see occurs around 1,200 pixels wide. Here the longer text items start to get cut off and are no longer fully readable.

screenshot

To fix this, we need only to adjust our font size. I target an area just before the problem occurs (1,220px) and set my fonts to 10px.

/* MEDIA QUERIES*/
@media only screen and (max-width : 1220px),
only screen and (max-device-width : 1220px){
	nav li a {
		font: 400 10px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	}
	
	nav small {
		font: 100 10px/1 Helvetica, Verdana, Arial, sans-serif;
	}
}

With that change, the problem no longer occurs. As we reduce the size of our window, the menu responds and reduces the font size to accommodate the size.

screenshot

930px

Once again we reduce the size of the window and find that things get hairy around the 900px mark. We’ve got lots of problems with overflow here:

screenshot

Rather than making the font-size smaller and smaller until you can’t see it anymore, this time we’ll completely reflow the menu so that it splits into two rows. Now each link can be nice and large given that this might be around the point where tablet sizes kick in.

@media only screen and (max-width : 930px),
only screen and (max-device-width : 930px){
	nav li a {
		width: 25%;
		border-bottom: 1px solid #fff;
		font: 400 11px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	}
	
	nav li:last-child a, nav li:nth-child(4) a {
		border-right: none;
	}
	
	nav li:nth-child(5) a, nav li:nth-child(6) a, nav li:nth-child(7) a, nav li:nth-child(8) a {
		border-bottom: none;
	}
}

Notice that we had to rethink how our borders work this time as well. Now we need the top row to have borders on the bottom, but not the bottom row. Once again, pseudo class selectors make this pretty easy.

screenshot

580px and 320px

The two row format works well enough until we get below 600px and then all hell breaks loose. The thing just completely falls apart.

screenshot

To address this, we’ll finish off our media queries with a two column, four row format. We’ll use this layout for 580px below and bump the font-size down a little at 320px.

@media only screen and (max-width : 580px),
only screen and (max-device-width : 580px){
	nav li a {
		width: 50%;
		font: 400 12px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
		padding-top: 12px;
		padding-bottom: 12px;
	}
	
	nav li:nth-child(even) a {
		border-right: none;
	}
	
	nav li:nth-child(5) a, nav li:nth-child(6) a {
		border-bottom: 1px solid #fff;
	}
}

@media only screen and (max-width : 320px),
only screen and (max-device-width : 320px){
	nav li a {
		font: 400 11px/1.4 'Cutive', Helvetica, Verdana, Arial, sans-serif;
	}
}

Our menu now looks good even at a very small size. The downside is that it does take up a decent chunk of vertical height, but you can remove some of the padding if that bothers you.

screenshot

Selectivizr

screenshot

The astute observers among you will notice that we accounted for older versions of IE with the html5shim, only to follow that with some fancy pseudo class selectors that won’t work in IE either. To account for this, be sure to download and implement Selectivizr, a fancy JavaScript utility built specifically for bringing CSS3 selectors to IE6-8.

Conclusion

Responsive design is no walk in the park. It takes time, effort and plenty of “know how” to pull off effectively. I like to break it down into individual challenges like the navigation menu here and the image gallery in a previous article so you can learn the process without feeling overwhelmed.

You should now be well equipped to embark on your own responsive navigation menu project. Leave a comment below and show us what you come up with. What unique challenges did you face along the way?