CSS3Tutorial

Create a zoomable user interface

Share this article

David DeSandro reveals how to use CSS transforms to create a zoomable user interface similar to that of 2011.beercamp.com. In this tutorial, you’ll also learn how to use JavaScript to hijack scrolling to manipulate the zoom

This article first appeared in issue 216 of .net magazine - the world's best-selling magazine for web designers and developers.

With CSS3 transforms now supported in most major browsers, we have the delightful opportunity to create innovative layouts and interfaces. No longer are we shackled in our one-dimensional prisons, bound to the tyranny of vertically-scrolling sites.

With the site for BeerCamp at SXSW 2011, we at nclud recognised an ideal opportunity to bend some rules and try something new. I got the idea to leverage CSS transforms for the layout. Instead of the typical vertical scrolling site, where you traversed it downwards, this would could be traversed inwards. This is sort of design pattern has been categorised as a zoomable user interface or ZUI.

The BeerCamp at SXSW 2011 was an experiment in using CSS transforms to create a new interface design pattern

The BeerCamp at SXSW 2011 was an experiment in using CSS transforms to create a new interface design pattern. In this tutorial, we’ll build a zoomable user interface into a simple page. You’ll learn how to use CSS transforms to create a zoomable layout. You’ll also learn how to use JavaScript to hijack scrolling to manipulate the zoom.

Our example page lists the services of a web development shop. The services listed range from the broad to the specific. This content is well suited for a zoomable layout, as the subsequent sections are smaller segments of the previous sections. By placing one visual element inside another, you’re visually communicating the relationship between pieces of content.

Basic layout

Prior to the ZUI layout, the basic layout is designed for browsers that don’t support CSS transforms or have JavaScript disabled. The markup looks like this:

The first draft of the site has the content laid out in the typical vertical pattern. This version is necessary as this layout will be used by browsers that do not support CSS transforms or have JavaScript disabled. The rest of the effects will be built with progressive enhancement.

For the zoomable layout, we need to consider how each section fits inside one another. I’ve chosen a ratio of 3:1 for the proportion between the current section and its subsequent section. This means the parent will have enough room for content, and the child container will still be visible.

Each section will be 900px x 540px so it fits within most browser windows. Subsequent sections will appear to be one-third the full size, 300px x 180px. You’ll notice this space in the centre of each section has been reserved for the subsequent sections to fit inside once CSS transforms are put in place. (See Demo 1: Basic layout.)

Now we can start adding CSS transforms. First let’s add Modernizr so we have more control over how browsers will inherit their styles. I’ve opted to use a custom build from the Modernizr 2.0 beta preview that only tests for CSS 2D transforms and CSS transitions. After adding the Modernizr code to our scripts, we can target browsers that support transforms with .csstransforms in our CSS. To scale each section inside one another, they first need to occupy the same space. This can be done with absolute positioning.

Each section needs its own scale set. As the proportion we’re using is 3:1, each section will be one-third the size of the previous. This can be calculated as the inverse ratio to the exponent of the level’s zero-based index:

zoomScale = inverse ratio ^ zero-based-level

CSS3 is the principal technology that enables the ZUI for this page. We are leveraging scale transforms. The scale of the first level, #web-dev, is (1/3) ^ 0 or just 1, so we don’t need to set that superfluous style. The scale of the second level, #front-end, is (1/3) ^ 1 or 1/3 or in decimal 0.3333. The scale of the third level, #css, is (1/3) ^ 2 or 1/9 or in decimal 0.1111. We’ll apply this value to the various vendor-prefix transform CSS properties for all four of our subsequent sections. For the sake of brevity, the code example below only lists the unprefixed transform property, but in your actual code, remember to add all the vendor prefixed versions (-webkit-transform, -moz-transform, etc).

Each section is positioned inside one another using CSS scale transforms. You can see the second section within the first, and if you look closely, you'll find the others deeper within

Each section is positioned inside one another using CSS scale transforms. You can see the second section within the first and the others deeper within. Now we need to build a mechanism to let the user zoom in.

To zoom in to a section, we only need to apply its reciprocal scale to the sections’ parent #content. All child sections will scale up accordingly. The scale is equal to the ratio to the exponent of level’s zero-base index. The second section #front-end has a scale of 1/3, so it needs to be scaled 3x to bring it to 100% size.

zoomScale = ratio ^ zero-based-level

The scale to view the third level would be 3 ^ 2 = 9. For the fourth level, the scale would be 3 ^ 3 = 27.

(See Demo 3 – Fixed zoom.) Applying a scale that increases the size of container will zoom in on its content.

Applying a scale that increases the size of container will zoom in on its contentScroll

Leveraging window scrolling is a natural convenient interaction to hook zooming into. Along side clicking and pointing, scrolling is a natural interaction that anyone with a mouse or keyboard uses. Currently there isn’t anything to scroll, since the entire page is self-contained in that 900 x 540 area. But we can fake it by adding an empty element that has height, which will serve as our proxy. The markup will be added after #wrap.

</div> <!-- #wrap --><div id="scroller"></div>

In the CSS, set an arbitrary height on #scroller. 4000px works as an approximate height of the page before we add the scale transforms.

.csstransforms#scroller{height:4000px;}

But we don’t want the content to scroll with the rest of the page, so we can use fixed positioning to fix the actual content in its same place.

/* prevent content from scrolling */#wrap{position:fixed;width:100%;}

The page scrolls, but the content remains static. Now the fun begins as we jump into scripting.

JavaScript

The basic idea is that we are going to hijack the scroll event and do something with it.

Hijacking the scroll behavior enables users to zoom in to inner content

As all this script will only need to run if the browser supports CSS transforms, we can encapsulate our entire script in a self-executing function, which will only proceed if CSS transforms are supported.

I’m using a constructor design pattern, Zoomer, to do all the work. The Zoomer constructor requires a DOM node, specifically the content container, which will be passed in later. It holds properties like scrolled, which will be the vertical scroll position, levels, which is the zero-based number of sections, and the height of the page in docHeight. Most importantly, we pass it in as an event listener to the window’s scroll event.

The handleEvent method allows the constructor to be used as an event listener, so we can properly use this in window.addEventListener( 'scroll', this, false). If a method matches the event’s type, that method will be called. So we can bind Zoomer.prototype.scroll to the window’s scroll event.

If done correctly, when you scroll to the bottom of the page, you’ll zoom in perfectly to the last section.

Completion

Behold what you have accomplished! Not only have you produced a pioneer in the realm of web interface development, but you have built it in such a way that it will be fun to use and engaging to interact with. Well done!