Yes, you really can make complex webapps responsive

hero

Adioso used to be accessible on mobile devices in the flick-panning, pinch/zooming kind of way. Then I joined the team last October as a designer and gave the frontend a complete overhaul. The result? an improved aesthetic, but a worse experience for our mobile users. The penny dropped during one of our team planning meetings.

1 out of every 5 visitors is having a bad time.

In my haste to get the new design live, I had implemented a lot of responsive half-measures that had left the site completely unusable and broken on mobile devices. This article explains how we refactored an interface that wasn’t originally built with mobile in mind, to have a decent mobile experience. All while avoiding that dreaded term, complete rebuild.

For static websites, like most marketing or landing pages, responsive design can suffice existing as an afterthought. Often the content adheres to a nice grid, the columns of which can stack harmoniously as the browser window gets smaller. The Build Conf sites are always a great example of this (resize your browser width and you will see what I’m describing).

Information consumption is the focus for the designers of these websites. Visitors need to be able to digest content with low friction on any device they own. While responsive techniques are popular for brochure type sites, the question remains; How achievable are they for interaction rich web apps?

We had many discussions and debates on which path to take.

  • Spend a large portion of funds and time building an iOS app?
  • Redirect to a separate mobile website (maintain another code base)?
  • Were responsive techniques ready for a complicated interface?

As a team we ship often and the thought of building something in parallel over a long period of time was concerning. When possible, we lean towards iterative solutions that can be deployed intermittently. Not only do we enjoy the results of this short feedback loop, it’s critical for agility.

Our big challenges emerged from two crucial interactions in the Adioso experience: filtering and displaying itineraries. Filtering used the hover state, and itineraries were built with tables. It’s no surprise that both hovering, and tabular data are the achilles heel in responsifying content for both touch devices and desktop/cursor devices. Once we rolled our sleeves up, we noted that the learning curve was moderate but the tools available are ready. Here is what they are, and how we used them.

Foundation Framework

For the bulk of the work, Zurb’s Foundation framework came to the fore. Foundation (now in its fourth version) is a comprehensive mobile-first front-end framework. You include it in your project to gain access to helpers and defaults for forms, buttons, grid layouts, typography and much more. It can be compared to Twitter’s popular Bootstrap framework.

We wanted to implement this library with the lowest amount of site-breakage possible. Therefore we commented out every component we didn’t need to improve the mobile experience. The only things left were global (required variables), grid (to help the collapsing of divs underneath each other) and visibility (hiding classes for breakpoints).

Stripped

Brutal cull

Once this was included in our app, most of what was broken came from this css property:

* { Box-sizing: Border-box }

Whilst visibly this broke our layout, since learning what it does I wouldn’t start a project without it. In short it makes the calculation of the box-model more natural, or in Zurb’s words:

It makes math dead simple.

For a thorough explanation of the box-sizing property, check out Paul Irish’s article here.

Out of the box, Foundation sets up two breakpoints, named small and large. These breakpoints refer to the media queries that fire depending on the width of the device viewing the page. We added a breakpoint in the middle for tablets that we aptly named medium. The breakpoints are defined in the global.scss file.

Foundation is ‘mobile first’ in that you should be designing your interface first on small devices, then make cascading changes as you gain more real estate. By default, small is set to fire for devices smaller than 768px in width (ipad in portrait mode). We decreased this value slightly to match our responsive mixins, but I’ll discuss later.

Foundation shows the power of breakpoints with it’s use of the grid component. Considering that 100% width is equal to 12 columns (popular in grid frameworks because 12 is such a divisible number), we can tell the browser how many columns we want a particular element to span across, and which breakpoint we want that to happen. For example:

<div class='row'>
    <div id='foo' class='large-4 medium-6 small-12 columns'>		
    </div>
</div> 

This means that the #foo div will be full-width on mobile (small-mode), half-width on a tablet (medium-mode) and a third-width on a desktop or macbook screen (large-mode). By simply adding small/medium/large class names to the markup, I was able to quickly take our search form and make it respond to our breakpoints.

Large mode (zoomed out)

Large mode (zoomed out)

Medium mode

Medium mode

Small mode

Small mode

You’d be surprised how close your site is to becoming responsive once you harness these frameworks. In the case above, Foundation’s small/medium/large classes only help us with column widths. Often you will need more control. In small-mode for instance, we don’t just want the input fields to be full width, we also need less padding and a smaller font-size. We need some SASS responsive mixins.

Responsive Mixins

We use SCSS as our CSS pre-processor in conjunction with Compass. Compass provides a plethora of helpful mixins, but we need to define our own that work closely with the Foundation breakpoints. Ben Schwarz shows us a great mixin for defining various breakpoints. Using these mixins from this tutorial, and matching the media query values with our breakpoints, we get a mixin that targets our 3 modes.

@mixin respond-to($media) {
  @if $media == small-mode {
    @media only screen and (max-width: 420px) { @content }
  }
  @else if $media == medium-mode {
    @media only screen and (max-width: 768px) { @content }
  }
  @else if $media == large-mode {
    @media only screen and (max-width: 980px) { @content }
    }
  }
}

While our small/medium/large classes tackle element widths (column spans), this mixin gives us access to all css properties such as font-size and margins etc:

#foo {
  font-size: 1em;
  margin-top: 20px;
  @include respond-to(small-mode {
     font-size: .8em;
     margin-top: 10px;
  }
}

“But Daniel, there are still things that can’t be done with CSS!”

We came to this harsh realisation trying to make our more complicated elements mobile friendly, such as adding filters and displaying itineraries. The need for javascript-based media queries emerged. That’s where Modernizr came to the rescue.

Modernizr is a feature detection library that tells us what a browser can and can’t do. It’s been around for a while and has become a staple for many front-end developers.  It has an mq method for performing javascript based media queries. It takes a string argument (in the same format as a CSS media query).

Modernizr.mq('only screen and (max-width: 420px)');

You can probably guess what we did next. We set up variables for each mode that matched our responsive mixins and Foundation breakpoints.

var small_mode = 'only screen and (max-width: 480px)';   //  small
var medium_mode = 'only screen and (max-width: 768px)';  //  medium
var large_mode = 'only screen and (max-width: 980px)';   //  large

Now we can perform javascript based on any breakpoints. For example, in one case we wanted to remove the tooltip delay on mobile devices and allow it to take up more width.

if (Modernizr.mq(small_mode)) { 
   delay = 0;
   width = '95%'; 
}

So far we’ve gained a good coverage for most of the general layout issues across breakpoints. As we started to gain confidence, we approached the challenged identified initially, responsive itineraries.

Tables

Tables have become the thorn in the side of responsive web design. This is because tables are often horizontally hungry, present a lot of data at once and have rigid presentation rules. Flight itineraries are always presented in tables, and are the crux of the data we serve with Adioso. We knew this would be our biggest challenge.

Most solutions for responsive tables succumb to the fact that tables cannot collapse. Zurb even have a plugin that keeps 1 column fixed, and lets you scroll horizontally through the columns that spill out of the table. We played around with this but knew there was something fundamentally wrong with not displaying all the info that is crucial to our users.

To explore whether our small/medium/large classes could help us, we needed to ditch the table, tr and td tags and rebuild our itineraries with divs. The grid component of Foundation is infinitely nestable. This enabled us to carefully dictate which elements inside other elements would collapse. We also used our responsive mixins to apply floats and text-align values that helped balance the layout. The fruits of our labours are as follows:

Large mode (zoomed out)

Large mode (zoomed out)

Medium mode

Medium mode

Small mode

Small mode

We decided that a non-negotiable for our mobile experience was the ability to view a whole itinerary without scrolling. And for every element to be easily tappable. It was difficult to stay within our ‘vertical budget’. You learn to pinch padding and margins from each div to gain some vertical space, but in some cases you have to cull elements entirely. You may have noticed the lock and flight-details buttons missing for small and medium. This was done with the visibility component of Foundation. All you have to do is add a hide-for-small (or medium/large) class and that element will be hidden for that breakpoint.

This type of decision making process was common throughout the responsifying of Adioso. As a product designer, you need to consider how people consume information on different mediums. A fully fledged flight hunt with multiple leg locking and a variety of filters applied would be more suited to a ‘sit down’ desktop experience than an on-the-go mobile setting. That said, we tried to include everything possible where we could.

There’s still a lot of ground to cover, but as I pointed out, this is an iterative process and can be completed in stages. At the time of writing this article, we’re tackling modals, popup-filters and browser performance. It’s important to note that a responsive web-app is by no means a replacement for a native iOS or android app. You only need to wrestle with mobile browser memory performance to realise this. But with the emergence of tools like Trigger, who knows what the landscape will be like in a year or two?