Building progressively enhanced websites using bulletproof Backbone & CSS transforms

google lens

When you finish a project, you usually have a much deeper understanding of the project-specific issues than at the beginning. Perhaps you would even approach some stuff in a completely different way. That’s why you should check out if others have found better solutions for the problems you faced, irrespective if you can still change something on the code or not.

We often realize that the problem is not that the answers aren’t out there. We just don’t ask the right questions. Designing usable products and websites means to fully understand the complexity at hand and breaking it down to a simple, accessible and easy to understand solution. When I developed my blog, I started all over again… Twice.


This is rather technical post about learnings from developing my blog using HTML5 pushState, Backbone.js and CSS transforms. The goal is to progressively enhance the user experience without hurting SEO and wrecking up the user experience on older browsers.

It’s all about the idea

As an interaction designer I was obsessed of having a blog that feels like an app but still leverages the classical features of web browsers like the back and forward button.

Readability was kind of my inspiration. I wanted a list of blog posts that fetch articles on demand and smoothly fade them in, while remembering the scroll position on the overview’s page.

Whats the big deal?

Lots of websites using Backbone have central pages which control the behavior of the whole frontend. You might have something like www.backbone-site.com/#/showPicture. You would then land on the index which has a Router attached to it that ultimately triggers the showPicture method.

Now what’s the problem here? While this might work for a an app where you initialize your stuff in a specific file, this behavior is not efficient for websites (whether it is a good idea to use backbone for a normal website is another discussion). Why? Because you will land on the sites index that relies on triggering a Javascript method to actually display something (you can checkout a solution to avoid the initial fetch here. But even with this, it’s still far better to already have your data in the DOM).

My solution should address this issue and do the following:

  • Fully work without JS and progressively enhance the experience if JS and pushState is available
  • Load contents immediately so search engines can access them
  • Allow the users to directly load content without showing animations on load
  • Write initialization data in the head to instantiate models

I would soon find out that this approach has huge benefits. For instance I can turn off progressive enhancement for crappy browsers and users will never realize what they’re missing out.

My approach to bulletproof Backbone for websites

Dan Cederholm coined the term bulletproof web design. Among others, he meant to have forms that work with AJAX but fall back to the native HTML behavior if Javascript or the XMLhttpRequest object aren’t available. I wanted to incorporate this concept in my blog.

Building the fundamentals

We all know how nasty JS can get on bigger projects. Even though this was a small project, Backbone did an incredible job with its versatile features and I honestly think that it’s one of the best things to happen to the Javascript world. While my first version based on the classical Backbone procedure – i.e. loading models in a collection while instantiating them and then later rendering the data with underscore’s powerful templating engine – the solution that now runs on the server is much simpler. I won’t go into details on how Backbone works though (if you are looking for this, check out Addy Osmani`s book or Jeffrey Way`s tutorials on tutsplus.). I started developing two views without even thinking about the backend:

  • Blog posts view
  • Article view

I then included them into my wordpress theme. Everything was working as you would expect. A normal blog with some browsable articles. Nothing special so far.

Spicing up things

It’s about time we add some magic to this website. We’ll now have a look at those few lines of code that are responsible for bullet proof backbone, cross-browser css transforms and HTML5 pushState. Let’s start with the Router:

var Router = Backbone.Router.extend({
	initialize: function(){
		App.States.firstRun = true;
		$('a[class*=js-action]').click(function( event ){
			event.preventDefault();
			window.App.Routers.Main.navigate($(this).attr('href'), true);
		});
	}
});

Line 4 is the interesting part here. We will prevent the default anchor behavior (page reload) for all elements with a js-action prefix according to BEM’s js-hooks notation and let the router handle the request. We will then be able to easily target all anchor elements that add Javascript functionality to the application. After this, we easily setup HTML5 pushState that keeps the browser from putting a hashtag to the URL:

Backbone.history.start({ 'pushState': true });

If you now click on a URL that has the js-prefix, the router takes over and loads the corresponding article. If Javascript would somehow fail to load, users wouldn’t even notice and search engines won’t bother either. What’s nifty about this is the fact that if you are being directed from an external website to an article on this site, you will immediately see the article while the menu asynchronously loads in the background. You can try it out here.

Progressive Transitions

Now let’s have a look how you can get the smoothest css transitions using hardware acceleration on modern browsers and falling back to the default effects rendering engine of jQuery if the browser doesn’t support transforms:

if (!$.support.transition){
	$.fn.transition = $.fn.animate;
}

What’s happening here is pretty slick. jQuery tests for transition support. If it is available, it will replace the default animation library and replace it with transit, a fully cross-browser compatible css transforms effects library built upon jQuery. Keep in mind though, to always animate the x and y values. Transitioning margins will never give you such smooth animations. There are some browsers that don’t support x and y very well though. You can solve this by specifying an afterFinish handle that swaps the translate3d values with marginLeft values after the transition has finished. This will give you an even further benefit. Position fixed will work as expected again (it behaves differently after translate3d has been used). I don’t want to go too deep now but the following code explains the concept pretty easily (Line 8-11):

SlideLeft: function ( ){
	var self = this;

	self.$el.transition({
		x: -this.dimensions.x,
	}, self.options.animationSpeed, (function(){
		App.States.side = true;
		self.$el.css({
			'transform': '',
			'marginLeft': -self.dimensions.x
		});
	}));
}

That’s it pretty much. There is a lot of stuff I’m still improving here and there and of course there is some more complexity involved but the code shown above basically explains the concept. It’s really about having a solution that smoothly degrades if JS or key UX features aren’t available.

Thanks for reading, I hope it was somehow understandable. Feedbacks are welcome @twitter.