Recently, Cloud Four dev Matt Gifford built a slick, responsive off-canvas navigation enhancement for a project (we’ll be releasing the core code shortly, so keep an eye out for Matt’s post about that!).
The project followed the off-canvas menu metaphor for handling navigation on narrow screens. The baseline, mobile-first layout keeps the navigation out of the way—initially as footer nav with a button-ish jump link, then progressively enhanced to convert the link into a trigger element for the off-canvas menu—while a media query for wider screens formats the navigation on-screen.
A sketch of this layout adaptation:

In the past we’ve approached implementation of this as:
- Implement a baseline CSS layout that has a menu button.
- Use a CSS media query to adapt the layout for wider screens, hide the button, reflow the nav.
- Use JavaScript, often bound on window.resize, to check media query applicability and adapt behavior accordingly (sometimes alternately handled by looking at the width of the viewport and comparing it to media-query-defined widths).
Bolting on Behavior
Often by the time we get to the behavior implementation of our sites and apps, the process involves bolting on JavaScript as a follower of the CSS-based breakpoints that we’ve generated during the design process. We define the visual breakpoints of our stuff and the JavaScript is expected to use those to indicate how it should behave.
So we bump into things. We want the JavaScript to “know” about the breakpoints defined in CSS, and which media queries are presently active. This leads to heartache, and also some pretty clever hacks. We use matchMedia (or a polyfill to support same) to determine whether a particular, specific media query is active.
Thus: duplicating media queries in two places (CSS and JavaScript), a situation that makes a whole lot of us twitchy. I have definitely fantasized about future CSS module spec revisions that allow for naming and scoping of media queries, to make stuff like this better.
But hold on a minute. What are we trying to accomplish here? Does CSS really hold a monopoly on breakpoints? Should our behavioral components be entirely beholden to the specific formulae of our visual layouts?
(Re-)Defining Breakpoints
But why is the breakpoint for menu/navigation behavioral adaptation entirely linked to a CSS concern? Could there be better indications in the browser that the behavior change is appropriate, beyond a CSS media query or screen width? And, in that case, what does define the breakpoint?
This sort of thing was already on my mind when I started reading Stephen Hay’s excellent new book, Responsive Design Workflow. And Stephen is thinking about these things, too—except he’s already a lot further along!
Stephen’s way of defining a breakpoint is: “the points where certain aspects of a website or web application change depending on specified conditions.”
No mention of CSS there—because the picture is bigger than that. As Stephen continues on to say, “[a]nother reason to consider a more full definition of breakpoints is that CSS is not the only method used to implement changes when a breakpoint has been reached.”
Exactly!
Breakpoint Graphs
To express and plan breakpoints, Stephen advocates the use of breakpoint graphs, an adaptation on bullet graphs he’s invented to communicate both visual and behavioral aspects of breakpoints.
Along the “qualitative” axis (horizontal in these examples), one charts a scale. Often, this is a range of screen width resolutions—the way we tend to think about responsive design and breakpoints.
Using the process from above, we might have a breakpoint graph that looks like this:

Thus, we’ve expressed that we have a breakpoint at 40em that alters the page layout.
But Stephen’s graphs go further than this visual design element. Using qualitative “bands,” Stephen’s graphs can communicate behavior or other aspects, like so:

This is starting to move toward thinking about behavioral changes as well as visual ones, expressing explicitly that we want to adapt navigation behavior, but the breakpoint is still owned and defined in terms of the CSS breakpoint: 40em. So it’s natural that we’ve been creating media queries:
@screen only and (min-width: 40em) {}
and the JavaScript corollary
if (window.matchMedia( "screen and (min-width:40em)" )) { }
Behavioral Breakpoints
Looking at breakpoints in such a clear way inspired me. I’d seen the screen-width-resolution-style graphs before, but the qualitative dimension was new and exciting. In fact, it frees us from tying our breakpoints to a visual or CSS source at all.
What is the breakpoint, in the case of the navigation menu example here?
When implementing the navigation behavior, Matt chose to use the state of the triggering button as the indicator for which kind of menu behavior to apply. Button extant and visible? Convert the navigation behavior to the corresponding off-canvas menu. Button gone? Deactivate the off-canvas menu and use on-page navigation. Matt does this by observing the state of that button and reacting accordingly, not by duplicating or checking on the status of the CSS media query that put it there in the first place.
Building Behavior into the Process
A behavior-centric breakpoint graph for this could look something like this:

where the breakpoint is the state of the trigger button. Yes, the state changes at 40em as a result of a CSS media query, but it’s the state we care about, not the media query (or window width) that did it.
Stephen’s book does an excellent job of pushing the notion that behavior needs to be a part of our responsive design processes, integrated and partnered with visual design, not just adjunct to or beholden to it. Breakpoints span various aspects of the overall experience, and I’m glad Stephen helped me really understand this.
Thanks!: my gratitude to Stephen Hay for his personalized help in making breakpoint graphs, Matt Gifford for the off-canvas menu idea that got me thinking and Tyler Sticka for a bit of sketching help and proofreading.
Earlier this year Sara Wachter-Boettcher asked me to do a technical review of her upcoming book Content Everywhere. It was a tremendous honor. I’m pleased to see the book hits the shelves tomorrow. You can order it on Amazon today.
I was also asked to write a blurb for the book. I’ve never done something like that before. What I wrote probably wasn’t what the publisher was looking for, but it did capture accurately a moment I had while reading the book. Here’s the blurb:
OMG, so that’s what I’ve been doing these years! You know that unexplainable part where I divine order from the chaos of an existing site? Well, Sara makes it systematic, repeatable, and frankly better than anything I ever did. And if I didn’t find this book so damn useful, I’m pretty sure I’d hate her for it.
And that is exactly how I felt when I read what turns out to be pages 57 through 60 in the final book.
One of the things I have often done on projects is make decisions about what types of content warrant their own structure in the content management system and how they relate to one another.
The way I approached this problem was to spend a lot of time with the content of a site to understand how it is being used, how frequently different sections update, and how we might want them to work in the future.
Then after sufficiently marinating my brain in the content, I would propose what content needed their own records and what fields we were going to track for each.
What I was doing worked, but don’t ask me to explain it or worse, teach it to someone else.
That’s where Content Everywhere comes in. Sara distilled it to four key questions:
- Will this element be used for searching or sorting?
- Will it be used to relate this content to other content?
- Does it need to be extracted and displayed alongside other content?
- Does it need to shift, resize, or be removed altogether for some displays?
There’s more to it than that. Sara provides examples, interviews people who develop content models, and provides details on what you should do with the model. And that’s just one chapter in the book.
But for me, reading that section was an eureka moment. Sara had put in words what I had done on so many projects but had never been able to describe to anyone else. And it was so simple. So clear. I will be using these questions on future projects.
And yes, I kind of hate her for that. But please don’t let my jealousy stop you from checking the book out.
Good news today that Apple CEO Tim Cook has acknowledged the problems with the iOS 6 Maps application. Now everyone is anxiously waiting on Google to release a new native version of Google Maps so they can regain their lost functionality. But what many people are overlooking is that even if Google releases a native maps app, it won’t be able as useful as it once was.
One paragraph in Tim Cook’s open letter stood out to me:
While we’re improving Maps, you can try alternatives by downloading map apps from the App Store like Bing, MapQuest and Waze, or use Google or Nokia maps by going to their websites and creating an icon on your home screen to their web app.
He’s right that these are good alternatives. I’ve been using Mapquest for turn-by-turn directions on iOS for a couple of years now. It works really well.
But here’s the thing, even if you found a maps application that worked better for you in the short run, if you tap on an address in the contacts app, it won’t launch your preferred maps application. It will always launch Apple’s default Maps app.
The same is true for people who prefer to use Sparrow for email, Chrome for browsing, or a different calendar. There is no way to change the defaults for these activities.
And that’s the real shame. Based on features alone, someone might decide that they prefer a different Map application than the one Apple ships with iOS. The fact we can’t chose our default applications becomes even more frustrating when the default applications fail us like the new Maps application has done.
So Tim Cook is telling us that while we wait for them to improve Maps, we should try these other Map applications. My question to him would be, while we’re waiting, would you mind letting us select a different app as our default?
Earlier this year I was in the Cloud Four office with my iPad, so I decided to show @grigs what we’d done about orientation change for Lucid Meetings. He looked at it, then got kind of frowny faced and said:
“Do you need to use the browser forward and back buttons in Lucid?”
Naturally, this wasn’t the reaction I was expecting, but I said “Nope! Uh, why do you ask?”
And that’s when Jason suggested that, while the orientation support was fine, we were losing a lot of screen real estate to the fat browser chrome, and wouldn’t it be even nicer to have use of that space?
Thus began my foray into making a “traditional web application” perform double duty as an “iPad web application.”
1. Removing Browser Chrome
So, I wanted to remove the browser chrome, but what that meant was that I actually needed to do the following:
- tell the browser that Lucid Meetings was “web-app-capable” (yes)
- tell the browser what kind “web-app-status-bar” to use (black)
I added these lines to the HEAD element:
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
Then I refreshed and… nothing changed. Read, read, read. Oh, I see. I needed to save the application to my home screen by pressing that little icon thing and selecting the Save to Home Screen option (and, I thought, how many users will know to do this???). I relaunched the site from the saved icon and… victory! The browser chrome disappeared. We’re done. Moving on.
Not so fast, bucko.
2. Staying In The Web App
As soon as I clicked any link or button, the web app immediately closed and the site reopened in the browser. That was weird and unexpected. I never did find the exact answer as to why that occurs for internal site links, but it does and there are a couple ways to deal with it.
One way is to convert the site to a single page application with fragment links or AJAX callbacks. That’s exactly how the real-time meeting engine within Lucid works, so that part was golden. The other way is to bind to the click events and prevent the default browser behavior that causes Safari to open (remember, we are in web app, not in Safari, per se). For the multipage, non real-time portion of Lucid, we took the latter approach.
After a bit of hunting around, I found a neat little jquery extension that does what you need: https://github.com/mrmoses/jQuery.stayInWebApp
For various reasons I didn’t want to hook all the links, so I restricted the hook to those links with the “stay” class. Then I added the “stay” class to nearly every link in the application and added the following jquery snippet:
$(document).ready(function() {
$(function() {
$.stayInWebApp('a.stay');
});
});
With this jquery extension in place and those “stay” classes added, the Lucid Meetings application now stayed in full web app mode, and that part of the task was finally done.
3. Add to Home Screen
Now the question became “how do I provide a (non-annoying) hint to people to encourage them save Lucid to their home screen?” I normally don’t save web sites to my home screen unless there’s a compelling reason and I never in the past considered that the site might improve its behavior if I did so.
So I went hunting around again and found this little gem: http://cubiq.org/add-to-home-screen
The introduction for this script captures it all:
I found that many iPhone and iPad users don’t know that they can add their favorite web sites to the Home Screen and interact with them like standard native applications. This script helps them to discover this great feature and suggests the steps needed to add your web app to the dashboard.
This script works exactly as advertised and is highly configurable (to reduce the annoyance factor). It also works quite well with the “apple-mobile-web-app-capable” meta tag set to “yes.” That is, when you relaunch as a web app, the script is smart enough not to prompt you, yet again, to save to your home screen.
4. How to Sex it Up a Little
Having gone that far, I now wanted to glam up the web application a bit by adding a startup splash screen and an actual icon for the home screen (to replace the auto-generated screenshot icon). This is a well trod path, though I did have to futz with things a bit. In the interest of putting all the pieces in one place, here are a few more things we added to the HEAD element:
<link rel="apple-touch-icon" href="/images/lm57.png" />
<link rel="apple-touch-icon" href="/images/lm72.png" sizes="72x72" />
<link rel="apple-touch-icon" href="/images/lm114.png" sizes="114x114" />
<link rel="apple-touch-startup-image" href="/images/startup-iphone.png" sizes="320x460" media="(max-device-width: 480px) and not (-webkit-min-device-pixel-ratio: 2)" />
<link rel="apple-touch-startup-image" href="/images/startup-iphone4.png" sizes="640x920" media="(max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)" />
<link rel="apple-touch-startup-image" href="/images/lmsplash1004.png" sizes="768x1004" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />
<link rel="apple-touch-startup-image" href="/images/lmsplash748.png" sizes="1024x748" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />
The first three link elements refer to icons in the standard sizes defined by Apple: 57×57, 72×72, and 114×114. The device selects the icon that’s right for it, with the 114×114 icon working for the non-retina iPad. As you might tell, the Lucid web app mode is also setup for iPhones, though we haven’t yet built our dream user interaction model for handsets. More goodness to come!
The next four link elements refer startup images. Three of them are portrait mode images for the iPhone, iPhone with retina display, and iPad. The fourth is a landscape startup image for the iPad. Note that the sizes here, as shown in the “size” attributes, are absolutely required or the startup image will simply be ignored. Yep, found that one. A couple times.
While trying to get the right startup image to show I saw some references to the associated media queries, so we ended up adding them as well. I didn’t dive all the way in to see if both the “sizes” attribute and the media query are required, but I can say that the above examples all work on their respective devices.
5. How to Keep the Display from Scaling on Orientation Change
At this point we had a pretty nifty full screen web app, but unlike the game of Horseshoes, close enough isn’t good enough — so more tweaking and optimization were called for. In this case, I wanted the application to fit well into the display for both portrait and landscape modes, and I wanted the behavior to feel more like an application than a website.
The starting point for all this activity is to set the viewport meta tag to indicate that the application is sized appropriately for the display and doesn’t need to be scaled THANKYOUVERYMUCH. It turns out, however, that we were not sized correctly. Hmm. Fortunately for us only a minor amount of tweaking was required, so we made a few judicious changes and optimized our application so as not to exclude iPad compatibility. I think of this as mindfulness of design and it’s the same thinking we’ve applied when working on the IE7 user experience (yes, that does matter).
With the layout fixed, we added the viewport meta tag to set the initial scaling. For completeness, I’m also showing the CSS inclusion, which shows that we think in terms of landscape, followed by switching to portrait mode. That’s my desktop legacy shining through. Woot. Don’t worry, it works if you go the other way too. One special note: the “(max-device-width:1024px)” part of the media query is present to ensure this only applies to tablets. There are some technical (read: foreign technology integration) reasons why this needs to be the case, and this isn’t the right place to delve into those, but rest assured we’re drumming those out as well.
<meta name='viewport' content='initial-scale=1.0,width=device-width' />
<link type="text/css" rel="stylesheet" media="all" href="/css/landscape.css" />
<link type="text/css" rel="stylesheet" media="only screen and (max-device-width:1024px) and (orientation:portrait)" href="/css/portrait.css" />
So, we were all done, right? Not quite. With the iPad, an orientation change results in a scaling operation which means text either zooms in or out, which is quite disorienting for a carefully sculpted application! If you hunt around you’ll find quite a discussion about this, as well as some competing recommendations. From my perspective, an application is different than a website where scaling might be expected. Once again, the internet provides a solution in the form of some custom scripting.
The wonderful Stack Overflow captures this discussion and provides a solution: http://stackoverflow.com/questions/2557801/how-do-i-reset-the-scale-zoom-of-a-web-app-on-an-orientation-change-on-the-iphon
<script type="text/javascript">
/*
* This bit of code disables user scaling on iOS until the user tries to scale with pinch/zoom.
* http://stackoverflow.com/questions/2557801/how-do-i-reset-the-scale-zoom-of-a-web-app-on-an-orientation-change-on-the-iphon
*/
if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
var viewportmeta = document.querySelector('meta[name="viewport"]');
if (viewportmeta) {
viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
document.addEventListener('gesturestart', function () {
viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=10';
}, false);
}
}
</script>
As the embedded comment suggests, the script detects that we are on an iPad device and changes the viewport meta tag to disable scaling by setting the minimum and maximum scale to the initial scale. In addition, if the user performs a pinch/zoom gesture (I don’t see this working reliably with double-tap in practice) then we hook the gesture start and “unlock” the scaling capability. In this way, we are pushing our “don’t scale me” agenda, while leaving an option for the user to override our default behavior.
6. How to force a numeric keyboard
Okay, this is a small one, but it DROVE ME CRAZY. One easy thing you can do to say “I love you” to your users is to use HTML5 input types, such as email, url, date, number, and search. In addition to validation possibilities, devices with configurable keyboards may be able to configure themselves with the right keys to speed input. A number field probably doesn’t mean you need to see a traditional QWERTY keyboard.
On the iPad, a “number” input type activates a spinner control that allows you to select from a list. This really doesn’t work, for example, when entering a 16-digit credit card number. For the life of me I couldn’t figure out how to get what I wanted: a simple numeric keypad. It wasn’t until Brad Frost started a discussion on the topic that it clicked for me: add a pattern attribute to a text input field. Voila!
7. Resources Are Available
We’ve only scratched the surface for what we have planned for Lucid Meetings, but what I’ve discovered is we have a vibrant, noisy community of people ready to help. I want to highlight one resource in particular, the Mobile Web Best Practices site, which is a sort of meta resource for me. Grab a beer and check out the resource page. It’ll be a fun afternoon. See you on the other side.
Bruce Lawson has great post today called What Users Want from Mobile, and what we can re-learn from them. I highly recommend reading it.
Bruce quotes from a survey of mobile web users and pulls out a few highlights including a huge demand by users for performance. Bruce writes:
This tells us that speed is more important than aesthetics. So perhaps some of the time and effort put into media queries, viewports, avoiding scrolling, line length would actually be better employed reducing HTTP requests and optimising so that websites are perceived to render faster.
Exactly.
This echoes something Brad Frost and I were talking about the last time I was in New York.
If you could only do one thing to prepare your desktop site for mobile and had to choose between employing media queries to make it look good on a mobile device or optimizing the site for performance, you would be better served by making the desktop site blazingly fast.
Most mobile browsers are pretty good about providing tools to help someone utilize a design meant for desktop on a small screen. People can double-tap or pinch and zoom to see and read the content.
But if your site is a bloated mess, there is nothing people can do about it. There is no magical gesture that people can invoke to make something load faster if the developer hasn’t built the site for speed in the first place.
The only gesture they are likely to use involves a single, upright finger as they ditch your site for one that responds to their requests in a timely fashion.
Recent Comments