Try Tuts+ Premium, Get Cash Back!
A Flexible Approach to Responsive Navigation

A Flexible Approach to Responsive Navigation

Tutorial Details
  • Topic: Responsive navigation
  • Difficulty: Beginner
  • Estimated completion time: 30 mins

Many responsive websites provide a horizontal navigation bar on large screens and drop-down navigation for smaller viewports. It’s a perfectly decent approach, but it’s not without its issues. Firstly, devices don’t just come in large and small; they come in every size imaginable. Secondly, the navigation might well change over time. Thirdly, the layout or font size might vary across screen sizes. We’re going to do things differently…

Why not check (with JavaScript) to see how much of our the navigation actually fits in the space available? Once we know that, we can take items that don’t fit within the navbar and move them into a dropdown menu. On especially small screens, we can choose to put the entire menu into a dropdown.


Does the Navigation Fit?

Which type of navigation works best? The answer to this question depends on several factors, including screen size, font size, font family, number of nav items, and the context in which the navigation appears. These factors interact with one another, and they can all change.

Rather than considering each of these factors, we’ll simply look at the end result: do all the navigation items fit in one neat row, or do some get pushed down to the next line? If it’s the latter, how many items do fit? Armed with this information, we can choose the right navigation for the job.


Step 1: Choosing Breakpoints

Responsive design has two main features. Firstly, the design must be fluid: its width scales with the width of the browser. Secondly, there are breakpoints, widths at which the design changes through use of CSS media queries. For example, you might decide that when the browser is no wider than 480 pixels, a sidebar will move below the content and and headings will become smaller. You can have as many or as few breakpoints as you’d like.

Screenshots of Twitter Bootstrap at desktop and mobile breakpoints.

You can create a breakpoint by using a media query, like this:

@media only screen and (max-width: 480px) {
	/* This CSS will be applied only to phones and other small devices. */
}

So, how do we go about choosing breakpoints? One approach is to pick a few widths that match up with common device sizes.

Twitter Bootstrap has several breakpoints, including a “smartphone” breakpoint that is triggered on viewports no wider than 480px. It also has a “portrait tablets” breakpoint that is triggered between 768px and 979px.

You may notice that there’s no “landscape tablet” breakpoint in Bootstrap. That’s because the iPad is 1024 pixels wide in landscape – the same width as many desktop screens. That brings us to the main flaw with this approach: it involves making educated guesses about types of devices. A device that hits our “portrait tablet” might actually be a really big phone or a really small laptop.

Media queries don’t tell us what type of device is being used, but we can make an educated guess based on the device’s size.

An alternate approach would be to base breakpoints on the design and content. You could implement a fluid version of your design with no breakpoints, and then test it at different widths. When you encounter widths where things start to look strange, it’s a good time to consider adding a breakpoint. This is the approach favored by Ethan Marcotte, who coined the term “responsive design.”

Screenshot of the Boston Globe at three different widths.

Which approach is best? As with so many things in life, it depends. Are you using a pre-built framework like Bootstrap? Do you think it’ll be easier to explain responsive design to your team or to clients if you can talk about specific types of devices? If you answered “yes”, choosing breakpoints based on device sizes may be right for you. If you answered “no”, choosing breakpoints based on your design and content may give you more freedom and flexibility. That’s the approach that we’ll take in this tutorial.


Step 2: Writing the Markup

Let’s start building a simple page. We’ll build an “article” area that contains some content and a navbar. Next to the article, we’ll place an aside.

<article>
	<nav>
		<ul class="navbar" id="mainNavbar">
			<li><a href="/">Home</a></li>
			<li><a href="/products.html">Products</a></li>
			<!-- More nav items... -->
			<li><a href="/contact.html">Contact</a></li>
		</ul>
	</nav>
	<p>Here is our content.</p>
</article>
<aside>
	<p>Here is a side note about our content.</p>
</aside>

Include ten or so navigation items in this list, for the sake of the demonstration.


Step 3: Basic Styling

First, let’s style our list to look a navigation bar.

.navbar {
	background-color: #055;
	margin: 0;
	padding: 0;
	width: 100%;
	line-height: 1;
	overflow: hidden;
}
.navbar li, .navbar a {
	display: inline-block;
}
.navbar li {
	list-style-type: none;
}
.navbar > li {
	margin-left: .25em;
}
.navbar > li:first-child {
	margin-left: 0;
}
.navbar a {
	padding: .25em;
	text-decoration: none;
	height: 1em;
	font-weight: bold;
	color: #fff;
}
.navbar a:hover {
	background-color: #088;
}

We’ll also make our article stand out a bit, and display our aside as a sidebar. We’re using percentage values for our widths to make our design fluid.

body {
	font-family: sans-serif;
	font-size: 16px;
	background-color: #fff;
}
article {
	background-color: #eee;
	padding: 2.5%;
	margin-right: 1%;
	width: 64%;
	float:left;
}
aside {
	background-color: #ccc;
	padding: 2.5%;
	width: 25%;
	float: left;
}

Our end result should look something like this:

Screenshots of the design at small, medium, and large widths.

Step 4: Add a Breakpoint

When our width gets particularly small, the text wraps so much that it becomes difficult to read. We can fix that by letting both the article and the aside take up the full width, and pushing the aside down below the article.

@media only screen and (max-width: 550px) {
	aside, article {
		width: 95%;
		float: none;
	}
	article {
		margin-right: 0;
		margin-bottom: 1em;
	}
}
Screenshot of the demo in a small window.

Feel free to play with the window width and watch what happens. In general, as the window gets smaller, there’s space for less and less of the navigation. When you shrink the window to 550px, though, you actually gain some space for the navigation as the aside drops to the next line.

The number of items that we can display in the navbar depends on several factors, including the width of the window, the breakpoint, the font size, and the number of items in the navigation.


Step 5: Set a Fixed Height on the Navbar

In many cases, our navbar content doesn’t fit on one neat line. Let’s fix that!

If the user has JavaScript, we’ll hide items that don’t fit. We’ll show them in a dropdown menu later. If the user doesn’t have JavaScript, we’ll just let the navbar’s height expand as needed. It’s not pretty, but it’s at least functional.

To do this, we’ll download and include a custom build of Modernizr, a JavaScript library that allows us to test feature support in browsers. Our custom build tests for JavaScript support and touch event support.

Next, we’ll add a “no-js” class to our tag. If the user has JavaScript, Mozernizr will change that class to “js.” If the user doesn’t have JavaScript, the class will remian “no-js”.

<html class="no-js">
<script src="modernizr.custom.js"></script>

Note: For performance reasons, it’s usually best to avoid putting scripts at the top of the page. While the browser is busy downloading scripts, it will stop downloading other files and rendering the page. In this case, that’s actually what we want: we don’t want the browser to display anything until our .js class is in place. There is a small performance hit associated with this approach, which we’ve mitigated somewhat by using a custom build of Modernizr. If you just need that “no-js” class and don’t plan to use the other features of Modernizr, you could make your page even lighter by using this one-line wonder instead.

Now that we have a working .js class in place, let’s use it to hide any items that wrap to a second line.

.js .navbar {
	height: 1.5em;
 	overflow: hidden;
}

Step 6: Add FlexMenu

Next, let’s download flexMenu, a jQuery plugin that lets us check whether all items fit in the navbar and display the appropriate type of menu. We’ll also need jQuery itself. Put these scripts at the bottom of the page, so that they don’t block the page from displaying while they load.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="flexmenu.min.js"></script>

We also need to tell flexMenu to look at our menu and resize it as needed:

<script type="text/javascript">
	$('#mainNavbar').flexMenu();
</script>

Now, as we resize the page, a “more” link appears when some of the items don’t fit on the navbar. You can hover over or tap this link to display a dropdown with these items. When the page gets too small for more than one or two items to show in the navbar, we’re presented with a “menu” link. When you hover over or tap this link, all the navigation items appear.

flexMenu, working but looking pretty ugly.

We now have the behavior that we want, but the dropdown menu looks pretty awful. That’s okay – we’ll fix that in the next step.


Step 7: Style flexMenu

Now, let’s add some styles to make the dropdown look nicer. We’ll also add a CSS arrow next to the “menu” or “more” link to make it clear that these links will open a drop-down menu.

With CSS arrows, there’s no image to load, and the arrows stay sharp on high-resolution displays. This approach works in all modern browsers as well as IE8. If you need to support IE7 or earlier, be sure to include an image-based fallback.

.flexMenu-popup {
	padding: 0;
	background-color: #088;
	margin: 0;
}
.flexMenu-viewMore > a, .flexMenu-viewMore > a:hover {
	background-color: #5599AA;
}
.flexMenu-viewMore > a:after {
	display: inline-block;
	content:"";
	border-left:0.3em solid transparent;
	border-right:0.3em solid transparent;
	border-top:0.4em solid white;
	margin-left:0.4em;
	position: relative;
	top: -.1em;
}
.flexMenu-viewMore.active > a, .flexMenu-viewMore.active > a:hover {
	background-color: #088;
}
.flexMenu-popup > li > a, .flexMenu-popup > li {
	display: block;
}
.flexMenu-popup > li > a:hover {
	background-color: #3aa;
}
Screenshot of our popup menu, looking pretty decent!

Step 8: Adjust for Touchscreens

Now we have a toolbar that works well on a variety of screen sizes, but it’s a little difficult to use on a touchscreen. Touchscreens don’t provide as much precision as mice and trackpads, so it’s easy to miss and tap the wrong item. To fix this problem, let’s make the links a little larger and move them a bit further apart.

Modernizr, which we added earlier, provides a CSS class for each feature that it checks for. On devices with touchscreens, it will add the class “touch.” On devices without touchscreens, it will add the class “no-touch”.

.touch .navbar {
	font-size: 1.25em;
}
Mmm Big Type

Because we’ve expressed all our sizes in ems, we can scale the whole navbar up or down by its font size. The one line above (ok, techincally three the way we’ve spaced it) adjusts not only the size of the text but also the size of the arrows, the navbar’s height, the padding on the links, and the margin between items.

Note: The .touch class indicates support for touch events, which lets developers build touch input into web applications. Devices that have both touchscreens and mice (e.g., Windows 8 slates) will most likely get the .touch class. Most touch devices simulate mouse events for compatibility purposes, so there’s currently no way to use Modernizr to detect devices with both touchscreens and mice.

Screenshot of our responsive navbar on an iPhone.

Step 9: Fix Cross-Browser Funkiness

If you pull up the demo so far in IE7, you’ll probably notice two problems:

  • As you resize the window, the sidebar sometimes gets pushed below the article before we hit our breakpoint.
  • The drop-down menu is one pixel below the bottom of the “more” link, so the menu disappears when you try to hover over it.

The first problem is due to IE7′s handling of percentages. If it calculates a value that inclues a fraction of a pixel, it always rounds up. The second problem is probably caused by a similar issue.

To work around this issue, we can set a fixed width for our page in IE7. To do this, let’s first add a “less than IE8″ class at the top of our page.

<!--[if lt IE 8]>    <html lang="en-us" class="no-js lt-ie8"> <![endif]-->
<!--[if gte IE 8]><!--> <html lang="en-us" class="no-js"> <!--<![endif]-->

Next, let’s add some CSS that sets a specific pixel width for the article and aside in IE7. The total width of these areas, plus the padding, margin, and allowance for rounding up, comes out to 901 pixels.

.lt-ie8 .flexMenu-popup {
	margin-top: -1px
}
.lt-ie8 article {
	width: 600px;
}
.lt-ie8 aside {
	width: 200px;
}
.lt-ie8 body {
	width: 901px;
}

You also might notice that on iOS, the zoom level changes when you rotate the device from portrait to landscape. To fix this problem, just grab this JavaScript-based fix and reference it at the bottom of the page. This issue has apparently been fixed in iOS 6.


Further Reading

Brad Frost’s articles Responsive Navigation Patterns and Complex Navigation Patterns for Responsive Design cover a wide variety of approaches to navigation on responsive sites.

Luke Wroblewski is also a great resource for responsive and mobile design patterns. In particular, his Off Canvas approach is pretty clever: it places navigation and other secondary content offscreen on small devices. When the user taps a button, the content slides in from the left or the right.

These resources don’t provide an exhaustive list, of course. Responsive design is a still a young field, and you can expect loads of cool new design patterns to appear in the future. Maybe some of them will be yours!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://jessesnyderdesign.com Jesse

    Great tutorial! I was just looking for something like this.

    So, when the navigation scales down and it all goes into the “menu” choice, rather than the full bar, could you also replace the word “Menu” with an icon to suggest a menu that way further reducing clutter on the screen at small sizes?

    • http://tomm.us Thomas Smith

      Nice idea, I guess using a media query and content:url() to bring in an image to display before? Like

      @media only screen and (max-width: 480px) {
      li.more {
      visibility: hidden;
      }

      li.more:before {
      content: url(image);
      visibility: visible;
      }
      }

    • http://www.ryandebeasi.com/ Ryan DeBeasi

      Sure! Just apply a the icon as a background image on the class .flexMenu-viewMore, and hide the text either by using text-indent: -999em; or by changing the text in the plugin’s options. (Those options are described on the plugin’s GitHub page.)

      • http://www.ryandebeasi.com/ Ryan DeBeasi

        Additionally, if you want to add the icon only for the “menu” popup and not the “more” popup, you can use the class “flexMenu-allInPopup”. This class is added only when the plugin moves all the navigation items under a “Menu” link.

  • http://php-sri-lanka.blogspot.com Upeksha Wisidagama

    Excellent Tutorial! Well written. Learned a lot! Thanks Ryan.

  • ReTox

    Does flexMenu work with nested menus or this is just for list items one level deep?

    I’m afraid if it doesn’t – there is no use for this kind of plugin

    • http://www.ryandebeasi.com Ryan DeBeasi

      That’s a fair criticism; it’s definitely a limitation of this approach. It probably would not work well if second and third levels of navigation were nested inside the first level. It does work well if you keep lower levels of navigation elsewhere – say, in a sidebar, or in a second-tier navbar.

  • http://www.brookswebdesign.net Maria

    This is a very clean neat approach! It’s so much easier to check the fit and know what we have to adjust…then to code for all the other factors. This way we just do a little extra CSS adjustment for the sub-menus.

  • http://falconcreativestudio.com Masood

    Its awesome tutorial for web developers.. i’m really happy to be a fan of tuts-plus..Cheer up guys…!!!!

  • MC

    Brilliant exactly what i needed for a new project.

    Why fixed height in em?

  • mitch

    Great tutorial however you missed one very important point – the initial view and scale… which without would look no different on touch devices.

    Don’t get me wrong, this site has great tutorials and references however as a premium member I’m disappointed by the amount which is left out and or forgotten and most often not thoroughly checked.

    Nice tutorial though,
    regards

    • http://www.snaptin.com Ian Yates

      Are you referring to the viewport metatag? It’s there (check the source or demo) but wasn’t specifically mentioned in the tutorial..

  • http://think360studio.com/ Think360studio

    Hi Ryan. Nice post. Thanks for sharing this post and for downloader also.

  • Linish

    Brilliant tutorial! +1

  • http://idehdesign.com idehdesign

    Brilliant

  • http://www.creativemanner.com ozgur coruhlu

    Ryan thanks for the awesome tut dude. I think I know what I will be using for my next project. Great one +1

  • http://stylewired.com Chris

    Hi Ryan,

    Thanks for the great tutorial. I’m just trying this out now and have it implemented it. However the flexmenu js is replacing my navbar ul with a “Menu” link, which displays the whole menu on click. Seems a bit strange. It does this in all screen widths. An ideas?

    Cheers,

    Chris

  • Pingback: Big Menus, Small Screens: Responsive, Multi-Level Navigation | Webdesigntuts+

  • Pingback: Tweet Parade (no. 37 Sep 2012) | gonzoblog.nl

  • Avon Gracias

    This Tutorial is awesome… However I tried adding submenus to this and no luck. Could submenus be added to the main links?

  • http://twitter.com/orosznyet ONet

    I had some issue with find(“li:last-child”).

    If you use submenus (ex.: ul#menu li ul.submenu li) the the find(“li:last-child”) will match in the submenu items too. Here is the modified file working with submenus: http://orosznyet.com/community/flexmenu.js

  • Pingback: Remotive Project | Build a Responsive Navigation

  • Pingback: w3talks Делаем адаптивное меню » w3talks

  • Pingback: Examining Responsive Navigation Patterns by Steven Bradley « Secret Box

  • Vishant Sanghavi

    Hello,
    This is very good script for creating responsive menu.
    I am having one propblem with this menu.
    I am not able to call any page , which is under flexmenu option.
    Please let me know what should I do.
    Thanks.