menu

A Responsive Design Approach for Navigation, Part 1

Posted by Maggie on 02/27/2012

As we create responsive websites, we must consider a number of factors
to make sure both the design and code are as bullet-proof as possible:
the design must scale across a wide range of screen sizes from mobile to
tablet to desktop; and the code must start with a mobile-first approach,
work well for screen readers or with JavaScript disabled, and be robust
enough to adapt to differences in text size and rendering across devices
or user settings.

Adapting site navigation to be understandable, usable and attractive
across a wide range of devices is particularly challenging. On the
Boston Globe site, we gained some key
insights about how to build robust mobile-first responsive navigation
systems. This is the first in a series of articles in which we’ll
explore navigation design techniques from very simple nav bar design, to
complex multi-level hierarchies, search, and fuller featured navigation
systems.

Using progressive enhancement, CSS media queries, and a little
JavaScript, we built a navigation list that adjusts to fit the size of
the screen and adapts to differences in text sizing. View the
demo

We’re in the process of rethinking our site design, so we used our own
navigation list as a test case. It consists of 5 options — What We Do,
What We’ve Done, What We’re Thinking, Who We Are, and Contact Us. These
options can take up a lot of space on a tiny mobile screen, so our goal
was simple: for smaller screens we’ll make the navigation as compact as
possible to save room for the main content, and on larger screens with
ample space we’ll display all options in a horizontal bar for quicker
access.

We marked up a standard unordered list of links, usable on any device
that renders HTML, layered on a few mobile-friendly styles to make the
hit area bigger for finger taps, and then added a little JavaScript to
transform the list into a custom dropdown menu. Users that don’t have
JavaScript enabled will just see the full list — no harm, no foul.

For larger screens, we used CSS3 media queries to display the options in
a horizontal bar. We wanted to ensure that the screen has enough room to
display the entire bar in a single line (we don’t want the nav or its
options to wrap because, in this case, wrapping would make the page look
broken or mess with the navigation hierarchy), so we estimated the
minimum width value: width of the horizontal navigation bar + the width
of any neighboring elements + a little wiggle room. We tested this
breakpoint across a wide range of browsers, and increased the value when
we saw options wrap.

We could’ve stopped there, but we can’t forget that screen size is just
one of many factors that effect how a layout is displayed. Take text
rendering, for example: the type, size, and shape of fonts can vary
among operating systems/browsers, and users have the option to adjust
sizing with browser controls. (And, as Stephanie Rieger notes, the list
of user-controlled variables is
growing.)
So, while it makes sense to use media queries as a baseline for altering
the layout, we made our navigation bar feel smarter and more responsive
with a little JavaScript to detect whether the horizontal nav bar fits
in the available space, regardless of screen size; when the fit test
fails, the navigation remains a custom dropdown menu.

The final result is a navigation list that’s optimized for use on a wide
range of screen widths and with variable text sizes. We’ll walk through
this example in detail below.

The menu is a list of links, so that’s what we’ll use: a standard
unordered list with anchor tags. We’ll group the list with a heading to
identify the navigation element, “Sections”, and give the outer
container a descriptive class for scoping styles, nav-primary.

To round out our example, we’ll add the Filament Group logo and a small
block of content. For screen readers we’ll provide a “skip navigation”
link so that they don’t have to navigate through the list on every page.

We’ll start with a few basic styles that make the links easier to read
and tap on small screens (a block display property and padding, and a
font-size that’s large enough for finger-based gestures), and a few
additional styles to make the list easier to scan (borders between
options, and bold text for the active option). We’ll also hide the
“Sections” heading (h3), but in a way that keeps it accessible to screen
readers.

We’ll add a few more rules to style the overall page, logo and content
blocks, and another rule to make the navigation play nicely with them
(clear the logo, and add space before the content). We want the “skip”
link to be available to screen readers but hidden from everyone else, so
we’ll position it off the page.

Our page looks nice at smaller screen widths, but on a larger screen,
each option stretches to fill the space and the navigation dominates the
page.

To fix this, we’ll add a couple of rules, scoped within a media query,
to display the navigation as a horizontal bar in screens that are 640px
wide and larger (640 = full width of the navigation + body padding).

We’ll float each navigation option to the left, remove the border
between options, and assign a slightly smaller font size. We’ll also
float the navigation block itself and the list container so that they
fully contain their floated child elements.

When the screen is wide enough to fit the logo and nav side-by-side —
910px, specifically — we’ll float the navigation block to the right. We
arrived at that number by adding the width of the logo (250px) to the
width of the navigation list displayed horizontally (640px), plus a
little extra margin to account for slight browser rendering differences
(20px).

Our navigation now assumes one of two forms in response to the screen’s
size: a list on smaller screens, or a horizontal bar on larger screens.

As it stands, our navigation looks pretty good on a range of screen
sizes — but there is definitely room for improvement. On small screens,
our navigation list still takes up a good chunk of vertical space, and
on larger screens, our horizontal navigation bar wraps when we increase
our browser’s text size. Bump up the text a couple of times on a
tablet-sized screen and the options wrap to a second line:

And at larger sizes, the entire navigation bar wraps under the logo,
creating odd gaps of white space:

So we have two issues left to address:

transform the navigation into a compact, custom dropdown menu when
screen space is limited, and

create a test to determine if navigation options will fit on a
single line, or if the entire bar will fit next to the logo on
larger screens; if not, show the compact menu.

We’ll write styles to hide all options by default except for the active
option, nav-current, and display the “Sections” heading as a clickable
dropdown button positioned to the right. We’ll add another class,
expanded, that we’ll toggle with JavaScript to show/hide the full list
of options.

Note that these styles are all scoped to the class, nav-menu. Later,
our test logic will toggle this class on the body tag.

We’ll bind a custom event, “testfit”, to the navigation container. We’ll
trigger this event when the page loads, and when the screen changes size
or orientation. (A nice side effect: desktop browsers that support text
zooming, like the latest versions of Chrome, Firefox, and Opera, trigger
the resize event — and this test — when the user increases or
decreases text sizing with browser controls or key commands.)

When the test passes, we’ll add the nav-menu class to the body. One
of the following conditions must be met for “testfit” to pass: when the
entire nav wraps to a second line under the logo, or when any of the
navigation options wrap to a second line. In both cases, we test for
wrapping by comparing top offset values, which can be effected by
changes in screen or text size. When the navigation’s top value is
greater than the logo’s, the navigation has wrapped to the next line;
when the last navigation option’s top value is greater than the first,
it’s wrapped.

We’ll also remove the nav-menu class from the body to reset the
styles before the test is run.

The pattern discussed here is one possibility for coding a responsive
navigation list. We hope to discover more, and will update our
RWD-Nav-Patterns git
repository as we
build additional examples.

This demo code is open source and available for download. Feel free to
put it through its paces. If you use it and see room for improvement,
please submit a pull request and we’ll review it as soon as possible.

A few nitpicks/questions:
- why a :hover style for anchors but no :focus and/or :active?
- do you consider having the skip link come back onscreen on :focus for sighted keyboarders, or do you use a separate solution on a real page for them? (of course those using a webkit-based browser get zero keyboarding benefit from skip links but everyone else does...)
- why force the user’s body font size to almost half what they’ve got set as their default? It seems to cause you to need to set all your font sizes to “ginormous” just to make anything readable. I mean the dreaded “62.5%” thing. Why not start at “100%” and use “1em” as your base, and go from there?
- Your JS looks like it’s probably pretty small, and it’s doing little more than waiting for events and then adding/removing classes from at most 2 elements, but the code included suggests jQuery… while mobiles could be grouped as “has JS” or “doesn’t”, of the group “has”, we are still dealing with things like battery technology, wireless internet connections and limited CPU… are you using jQuery Mobile, or jQuip, or xui.js, or some other made-for-mobile light-in-downloads framework? Is it better to try to avoid having the user download a framework at all if they’re likely to be using a mobile? (even tho bandwidth and screensize don’t really have such a relationship, but we make these assumptions when other info is scarce.)

@Stomme, you raise some great points.
- I missed the :focus styles, thanks for catching that!
- in general, setting the body size to 62.5% makes the math easier when using ems—but, to your point, that’s less true now that em-based media queries are in play, as some browsers don’t listen to the body size when interpreting them. So, yes, we should probably move away from the habit of resetting the body size and pull out our calculators again.
- good point about JS and performance. If this nav element is the only (or one of a few) simple JS-based components, then no, it’s not worth loading an entire library. We tend to build complex responsive web apps and sites, which benefit greatly from having a library in place, and the use of jQuery here is an artifact of that. If you have ideas about creating a non-jQuery version, have at it!

@Brad, I saw your post—it’s really helpful (and interesting) to see the current state of affairs with responsive nav patterns. And thanks for the mention!

@Maggie
re calculating with em’s: easiest not to. Just assume someone’s default is 1em, and for you that might be 16px, but who cares if everything is also sized in em’s. For me, 1em is about 20px. Bad eyes. Unless images are involved, don’t worry about what it ends up as pixels: that’ll be different per user anyway.

Have you seen David Hund’s responsive menu at valuedstandards.com? It relies on :target so without some JS it won’t degrade well, but otherwise is similar to this menu.

Oh woops, wanted to also mention:
I once tried Nicholas Zakas’ isMedia() function
https://gist.github.com/08602e7d2ee448be834c
to only load jQuery if the viewport had a minimum size, with the assumption that any JS running on smaller screens would be minimal and library-less.

Didn’t work for me as jQuery seemed to want to wait for page load event, not get called after it, but I chalk that one up to my limited JS and page-loading-events knowledge. But having the library only get called and loaded when a screen (whom I will assume then might not be on batteries, has a good internet connection and the kind of CPU found on larger internet-accessing machines) is large enough and then also call more complex scripts to do more fancy silly things… sounds like a nice thing to try. Stephanie Rieger makes a nice point about how wrong that assumption can be but without access to lots of server-side logic and testing, as a front-ender this is something to try.

@Stomme, thanks for sharing your thoughts on font sizing in the CSS. We’ve found that the best solution is the one that best supports the design and development of the project. Sometimes that involves resetting the font size, sometimes it doesn’t. Our primary goal is always to make sure that the page is legible, usable and accessible on the widest range of devices.

To Scott’s point, we’re very big on making sure assets are loaded in a smart way. I intentionally didn’t include that in this simple code example to keep the focus on how to construct the nav.

What we did (and admittedly this might not work on all websites) was this. First, style nav links as 44x44 icons with tiny print beneath: as it turns out, this looks good on *all* screen sizes and is easy for CSS. Next, rethink what the user *needs* on each page, and provide only those nav links. Eliminate the need for dropdowns. On the home page, for example, provide only the dropdown “headers” (main topics). Once a topic is chosen, on its page, show a “home” link plus links to other pages in the same topic (plus, optionally, contact). Great bandwidth savings, too.

@fjpoblam, generally speaking, mobile users want access to all content (and nav options), just presented in a usable format for their device. Limiting what’s available to them on mobile could lead to a frustrating experience.