Pull Down for Navigation

  • Tom Kenny

Mobile screen real estate is at a premium and one of the biggest problems to solve is how to display navigation when screen widths become quite narrow so I decided to have a look and see what I could come up with using some nifty CSS and a touch of jQuery.

Trying to condense a navigation down onto a mobile screen tends to push the actual content of the site down and out of initial view. Luke Wroblewski has explored some solutions in Mobile First but I’ve developed something that I haven’t come across just yet. In fact, in the process of writing this tutorial, Brad Frost released a great roundup of responsive navigation patterns so I suppose this could be an additional technique to add to his list.

Pull Down to Refresh

Pull down to refresh was a concept first introduced to the mobile world by Loren Brichter in his Twitter app, Tweetie as you can see in the screenshot here. Basically, pulling down on the list view when at the top updates the content. A simple and effective way of removing a button from the interface and a great use of touch screen input. This technique has been widely adopted amongst a range of different apps.

Pull Down for Navigation

You can see where I’m going with this but the whole thinking of replacing a UI element with a natural gesture (scrolling on a touch screen is about as close to a ‘natural gesture’ you can get in technology) is very valuable on a mobile screen.

Demo

Obviously the demo is best viewed on a smartphone device but hasn’t been tested on many Android as I devices I don’t have access to many! Works fine on iPhones. I’ve added the code onto GitHub but I’ve only just started using it. Right now it feels like a different language but please feel free to do whatever you need to do there if you want to improve it or use it for whatever you want to use it for.

Currently this exists as a great solution to single level navigation only. I haven’t tried to solve the second (and possibly third) level navigation just yet as I wanted to get this idea out as soon as possible.

Step 1

The HTML

<nav id="mobile">
	<ul class="group">
		<li><a href="#">Home</a></li>
		<li><a href="#">Portfolio</a></li>
		<li><a href="#">Blog</a></li>
		<li><a href="#">About</a></li>
		<li><a href="#">Contact</a></li>
		<li><a href="#">Twitter</a></li>
		<li><a href="#">Dribbble</a></li>
		<li><a href="#">A Longer Navigation Item</a></li>
	</ul>
</nav>
<div id="site">
<header>
<nav id="main">
	<ul>
		<li><a href="#">Home</a></li>
		<li><a href="#">Portfolio</a></li>
		<li><a href="#">Blog</a></li>
		<li><a href="#">About</a></li>
		<li><a href="#">Contact</a></li>
		<li><a href="#">Twitter</a></li>
		<li><a href="#">Dribbble</a></li>
		<li><a href="#">A Longer Navigation Item</a></li>
	</ul>
</nav>
</header>
<div class="container">
	<h1>Pull Down for Navigation Demo</h1>
	<p>Content here</p>
</div>

As you can see we have two nav elements. One is the main navigation which will be hidden at mobile widths and the other is the mobile navigation which will be hidden at all other widths and only shown when the browser window becomes too narrow.

Step 2

The CSS

#mobile { visibility: hidden; background: #21BBE1; position: fixed; z-index: 100; width: 100%; }
#mobile ul { margin: 0; }
#mobile ul li { width: 50%; float: left; }
#mobile a { color: #fff; padding: 15px 1.5em; display: block; border-bottom: 1px solid rgba(255, 255, 255, 0.25); text-decoration: none; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; }
#mobile li:nth-of-type(2n-1) a { border-right: 1px solid rgba(255, 255, 255, 0.25); }

#site { position: relative; z-index: 200; }

The first thing you’ll notice is the mobile navigation (#mobile) isn’t hidden by display: none; but by visiblity: hidden. This allows the jQuery code to work out what the height of the element is as we’ll want to move the page up by that amount to hide the navigation on load.

The #site div has to have a higher z-index than the navigation in order for it so slide over the top and the #mobile nav has fixed positioning so it doesn’t move it when scrolling.

/*
Media Query Goodness --------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------- */
@media only screen and (min-width: 320px) and (max-width: 480px) {

	/* Hide the main nav for mobile widths */
	#main { display: none; }

	/* Make the mobile nav visible for mobile widths */
	#mobile { visibility: visible; }

}

Above shows the code for displaying the mobile menu on mobile sized screens and hiding the main menu.

@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {

	/* Add the "Pull Down for Navigation" bar, all controlled via CSS including the text */
	header:before { width: 100%; display: block; content: "Pull Down for Navigation"; padding: 0.75em 1.5em; margin: -1em -1.5em 1em; color: #878787; text-shadow: 0 1px 1px rgba(255, 255, 255, 1); border-bottom: 1px solid #a6a6a6; background-origin: content-box; text-indent: 22px;
		background-color: #e3e3e3;
		background-image: url(images/pull.png), url(images/down.png);
		background-position: right center, left center;
		background-repeat: no-repeat;
	}

}

Using a media query with device-width allows us to target mobile devices specifically and add a bar at the top with the text “Pull Down for Navigation”. As the vast majority of smartphones use touchscreen inputs I think it’s appropriate to word in this way as that’s exactly what you’ll be doing. As you can see, the whole bar is controlled through CSS including the text through the use of the content property.

Step 3

The jQuery

As much as I would have liked to complete this solution with only CSS, it isn’t quite possible. What’s basically happening in the jQuery is that we’re getting the height of the mobile navigation and then moving the site up by that number of pixels. Follow the comments in the jQuery code to see how it works.

<script type="text/javascript" charset="utf-8">

    function checkWidth () {

    	// Get the height of the div and set it in a variable called mabileNavHeight to access later
    	var mobileNavHeight = $('#mobile').height();

        // Check to see if the browser width is less than 480px wide
        if ($(window).width() < 481) {
    	    $('#site').css({
    	    	// Change the CSS top value of #site to whatever mobileNavHeight outputs
    	  		top: mobileNavHeight + 'px !important'
    		});
    		// Scroll the window the height of mobileNavHeight
    		$(window).scrollTop(mobileNavHeight);
        }

    	// Check to see if the browser width is more than 480px wide
        else if ($(window).width() > 480) {
    	    $('#site').css({
    	    	// Make sure the top value of #site is 0 so we don't see the space for the mobile nav
    	  		top: 0 + 'px'
    		});
    		// Scroll to the absolute top of the page
    		$(window).scrollTop(0);
        }
    }

    $(document).ready(function(){
        // Check the window size when page loads
        checkWidth();
    });

</script>

Advantages and Disadvantages

Advantages:

  • Keeps the navigation at the top
  • Allows content to move up the page and become the focus
  • Uses simple touch based input

Disadvantages:

  • No solution for further levels of navigation
  • Maintaining two separate menus (although not a problem if dynamically generated)

Conclusion

It’s clear that there isn’t a one-size-fits-all approach to responsive navigation design so this is just another alternative. What I love about responsive web design is that there is no ‘right’ or ‘wrong’ way. In this case it’s the context of how it’s used that’s important, much like all design.

Update: I’m getting reports of it both working and not working on Android devices. Unfortunately I don’t have any way of testing on an Android device currently so would it would be great if someone can help test and get it working for more devices. Thanks! Grab the code from GitHub.

Update 2: Matthias Kretschmann on Twitter has informed me of some good news: