In previous articles in our Responsive Web Design series we discussed the challenges associated with Responsive Design that are typically encountered and some simple recommendations for being successful with Responsive Design. In this article we’ll present a design approach that can be used to improve the performance and maintainability of a website while retaining its ‘responsiveness’ to device diversification that we have begun referring to as Responsive+.
While working on this article, we came across a post by Boris Smus on “A Non-responsive Approach to Building Cross-device Webapps“. While it tries to address some of the underlying issues with Responsive Design, it still does not provide a comprehensive solution that solves all the issues.
Overview
Client-side optimizations are great but can only get you so far. Pushing unneeded code and content to a browser client is really not in the best interest of a responsible web developer. Not only is performance going to suffer, but also the past has proven that monolithic development is a sure recipe for maintenance nightmares. To counter these and other issues, many of which have been discussed in previous articles, our solution is to employ server-side processing to optimize module and content loading based on user-agent detection to augment a Responsive Design solution. By employing these practices, you can write better, more modular code that can minimize overall combined page weight, DOM, CSS and JavaScript parsing and thus greatly improving site load times and UI performance.
Responsive+ Design
Responsive+ is a design approach consisting of an aggregate of known techniques, frameworks and best practices that when applied appropriately provides great improvements to maintainability and performance of typical Responsive Design.
The base solution for a Responsive+ approach consists of the following elements:
- Device Classification – a simple way to classify devices as Mobile, Tablet and Desktop on the server-side using a combination of UA and Viewport size
- Responsive Modules – a modular approach for coding JavaScript modules
- UI Build Framework – a neat way to concatenate CSS and JavaScript files based on device classification
- Server-side Template Framework – to add simple conditional logic to format UI code based on device classification
In this solution, server-side device detection is used to tailor the base HTML page to a given device class. This tailoring includes customizing of JavaScript and CSS includes for the appropriate device class, as well as providing optimized assets for the target device class where appropriate.
In the following sections we will break down these steps and illustrate techniques that we use to implement them as well as some reasoning behind the purpose of each step.
Device Classifications
Rather than targeting all devices uniquely, the idea is to start out by deriving device classifications to which we would assign devices so that we would be able to deal with them at a broader, feature or form factor level. While yes, there are tons of different devices out there, with more being introduced almost daily, the goal is to organize these devices in a way that we can better address them as they relate to the goals of your given website. For the purposes of this discussion we will use the following taxonomy to relate to devices and their functionalities or form factor:
- Desktop – traditional pc, laptop or desktop
- Mobile – smartphone, iOS, Android, etc
- Tablet – iPads, Android Tablets, and Kindles
- Touch – Devices that support touch events
By applying these logical groupings to targeted devices you will be able to modularize much of the CSS, JavaScript, and even content including images. As new capabilities make their way online, such as speech and gestural control, this list can grow as the APIs emerge.
Client-side
Here’s a breakdown on how the front-end technologies would work in a Responsive+ developed site that supports the previously discussed taxonomy starting with CSS.
CSS structure for Responsive+
- base.css – normalizer, link colors, font faces.
- phone.css – layout
- tablet.css – layout
- desktop.css – layout
Each file other than base.css contains media queries for that size including portrait/landscape. As new devices demand new layouts, we need to create another file for that device. In essence, we have modularized the CSS based on device (or device classification).
A build script will then bundle these for the target required. For example:
- tablet.min.css = base.css + tablet.css
- phone.min.css = base.css + phone.css
- desktop.min.css = base.css + desktop.css
- full.min.css = a bundled version with all queries intact for device agnostic build
JavaScript structure for Responsive+
- core.js
- module.js
- module.capability.js (for each targeted capability)
The JavaScript is modularized through AMD, but rather than being broken down by device classification, it’s broken down by capabilities. As new capabilities are needed for each modules specific functional requirement, we simply add another file that only represents that change. This approach is outlined in an excellent blog post by John Reading.
UI Build Framework
Optimized and targeted UI payload builds are a critical piece of the Responsive+ approach. For each device classification, only the necessary files are sent down to the client. Server-side build tools such as ANT and R.js scripts, when automated from a build-time event, will minify and concatenate files and their dependencies.
The devices will only receive the css that is required for that classification, negating the need for excessive media-queries and css hacks. Since will are building these css payloads from a set of base files, we can avoid managing multiple forks of the same css.
In the case of the JavaScript, the build script will go so far as to remove the need for Require.js (or similar AMD management scripts) and AMD ceremony for devices that receive the module dependences as a single concatenated file. This has the advantage of removing another layer of complexity for browsers that may not benefit from supporting client-side dependency management due to anemic caches and high-latency issues with wireless carrier connections. We’ve released a fork of the R.js build script on GitHub that does so: R.js-Strip-AMD. Desktop versions and devices deemed to benefit greatly from caching (or are not subject to high-latency issues) would receive the client-side AMD dependency managed build, which would include Require.js.
Another area of “down-the-wire” optimization would be in regards to Modernizr. If we can detect capabilities on the server and we know that our target is a mobile device (or modern browser), we wouldn’t need client-side capabilities detection or HTML5 polyfills. Simply put, modern browsers don’t need Modernizr included in the core Javascript file. However, client side capabilities tests will be used for the fallback responsive version and Modernizr would get bundled in the full responsive version.
Server-side
Device detection is then handled on the server side, allowing for custom tailoring the HTML payload for the targeted device classification. The client-side code already does so much with regards to todays websites, leveraging server-side processing helps to alleviate some of that processing, especially processing that can be handled prior to handing off to the browser. This really helps to optimize the code and helps to insure that site performances is not being neglected.
User-agent Detection
Device detection is handled through User-Agent parsing and cataloging tools. You can leverage WURFL or dotMobi’s DeviceAtlas to provide cataloged references of devices based on their User-Agent string at runtime. The tool itself doesn’t really matter, rather it’s just the idea that you need to use a tool that is able to provide high level device determination based off of this string. Also it is important that you leverage a tool, rather than bake your own, as you don’t want to have to worry about keeping track of UA mappings, there are too many devices out there for anyone to manage on their own.
While UA sniffing has been seen as bad practice at times, there’s a growing sense that this may not be such a bad thing after all if used responsibly. There are more than a few bright people re-examining this technique as a viable solution, as evidenced by this nearly year-old thread started by Paul Irish and this interesting script, which is being quietly passed around.
Server Templates
The final piece of the high level solution is to leverage a server side templating library that supports basic display logic. This last component really depends on the type of site you are building. If you are building a small, quick and dirty site, you can probably get away with just coding straight PHP, ASP.NET or JSP’s, but for larger scale projects its best to set up an MVC and use something that forces a more strict separation between business logic and view logic. You can select a templating engine based on a technology stack used, for example, Freemarker for Java, Razor for .NET and Smarty for PHP, which makes backend code a lot cleaner and easier to maintain.
Tying It All Together
With these components all in place you can simply use the server side device detection to set variables that are used in the display logic of the server-side templates. This display logic is basically a series of if statements that are used to tailor HTML code such as CSS and JavaScript includes so that the appropriate libraries are added to the HTML. We can also apply this concept to other page assets such as images and videos, so that by dynamically setting these filenames on the server, we can deliver assets to the devices that are more optimized for them. Note this is still a responsive approach, as we aren’t talking about 100′s of images, rather it is serving up two, one high and one low resolution version. We also see this possibly extending in the future to supply ‘retina’ images for tablets and desktops as retina displays get greater market penetration.
Serving Right Assets
Addressing Capabilities
Serving Up Alternate Content
Sample Header Section using Freemarker for Assembly
<#include "macros/utils.ftl"> <!doctype html> <#if isDesktop()> <!--[if lt IE 9]><html class="no-js lt-ie9" lang="en"><![endif]--> <!--[if IE 9]><html class="no-js ie9" lang="en"><![endif]--> <!--[if IE 10]><!--> <html class="no-js" lang="en"><!--<![endif]--> </#if> <head> <meta charset="utf-8"> <#if isDesktop()> <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9,chrome=1"> </#if> <meta name="description" content=""> <meta name="keywords" content="" /> <meta name="robots" content="noindex, nofollow, noarchive, nosnippet, noodp, noydir, noimageindex" /> <meta name="googlebot" content="noindex, nofollow, noarchive, nosnippet, noodp, noimageindex" /> <#if isMobile() || isTablet()> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" /> </#if> <#if isMobile()> <link rel="stylesheet" href="/css/screen-mobile.min.css" /> <#else> <link rel="stylesheet" href="/css/screen-desktop.min.css" /> </#if>
Credit Where Credit is Due
We are not claiming to have discovered Atlantis. Anders M. Anderson first talked about Responsive Design + Server Side Components, an approach to combine responsive web design with server side components to make advanced responsive web solutions that work for all kinds of devices. What we have done is to streamline this methodology and create an architectural representation that we feel makes the solution scalable, maintainable and easy to implement utilizing any server-side technology.
Wrapping Up – More to Follow
By following this approach we have found that we are able to greatly streamline the overall page size and performance of the sites we are developing. In future articles we’ll break down some examples, including supporting data gathered from sites we’ve worked on. Until then, please feel free to share any experiences you have had with extending Responsive Design to the Server Side, we’d love to compare notes.
Contributions from: @sashasklar, @JohnnyReading, @mutootum