HTML5 Rocks

HTML5 Rocks

Get on the CSS Grid!

By Alex Danilo at

When building a Web Application, one of the first things that’s needed is a way to lay out the content of your app.

Lots of designers use imaginary grids to lay out content, whether it’s for a site or app. The CSS group has been working hard to make layouts easier, and as part of that have produced the CSS Grid Layout Module which is now emerging in browsers.

This feature is available to try out in Chrome behind an experimental flag. It’s also implemented in IE since version 10, and likely to be in most browsers soon.

Executive summary

  • CSS Grid Layout lets you define rows and columns for your layout
  • Grids can adapt to make use of available space
  • Content order can be independent from grid container display order
  • Layouts can change based on media queries, making responsive design easier
  • Content can overlap (enabling layout that’s impossible with other methods)
  • Grid Layout is fast

Here’s an overview video that explains a lot about how CSS Grid Layout works (slides are here):

Try it out

Using CSS Grid Layout in Chrome now is easy. First thing you need to do is turn on the experimental flag to enable the feature.

First load the chrome://flags URL and scroll down to the option to Enable experimental Web Platform features as shown below:

Image of experimental flag option

Just click Enable to turn the flag on, and you should see a prompt to restart the browser that looks like this:

Image of Relaunch button

Now just click the Relaunch Now button to restart your browser and you’ll be all set to try out CSS Grid Layout.

Let us know what you think

CSS Grid Layout is a great new primitive for web content, but as usual we’re all keen to hear what developers think about it. There are lots of ways to give feedback – leave a comment here, send mail to the W3C CSS Working Group list with “[css-grid]” in the subject line, log bugs, or blog and tweet what you think of it. We look forward to seeing the great layouts you build with this super useful new feature.

New Web Animations engine in Blink drives CSS Animations & Transitions

By Alex Danilo at

Users expect smooth 60fps animations in modern multi-device UIs. Achieving that level of performance with the web’s current animation primitives can be difficult. Fortunately we’re working on a new Blink animation implementation that just shipped in Chrome Canary!

What’s exciting about this is that it simplifies the internals of Blink and lays the groundwork for inclusion of new API features from the Web Animations 1.0 specification.

Until now, CSS Animations and CSS Transitions had been separate implementations, written independently, that didn’t necessarily play well together. For the past few years, browser implementers have been working together on a next-generation animation model with support for things like synchronization, chaining animations to run in sequence, seeking to arbitrary points in animation time, allowing animations to change speed, reverse and more.

The effort led to the formation of the W3C specification Web Animations 1.0.

The first step from the Blink team in getting Web Animations out into the world is replacing the existing Blink CSS Animations/Transitions C++ implementation with the Web Animations engine. Having reached that milestone now, we’d like as many developers as possible to check nothing’s been broken and more importantly to keep an eye on the implementation effort and give us feedback on what’s good/bad or might need changing.

Next up will be implementation of an API that lets you create, modify, and interrogate animations from JavaScript. The API is designed to let animations run efficiently (by using declarative semantics so Javascript manages creating animations but hands off control to the browser) whilst still exposing full animation control to the JavaScript developer.

We’re looking for active feedback on the proposed API to make sure we haven’t missed any features needed for powerful animation control. As with any new feature, the specification will continue to change, so now is the time to make your voice heard – ideally by subscribing to and contributing to the mailing list (and put [Web Animations] in the subject line so it gets noticed).

Try out the new engine that’s already powering CSS Animations & Transitions now and post any weirdness to the Chromium bug tracker so we know about it.

We’re excited to bring next-generation animation capabilities to Blink and look forwarding to working with other browser developers like WebKit and Mozilla who’ve also committed to implementing the new model.

Flexbox layout isn't slow

By Paul Irish at

TL;DR: Old flexbox (display: box) is 2.3x slower than new flexbox (display: flex).

A bit ago, Wilson Page wrote a great article for Smashing Magazine digging into how they brought the Financial Times webapp to life. In the article, Wilson notes:

As the app began to grow, we found performance was getting worse and worse.

We spent a good few hours in Chrome Developers Tools’ timeline and found the culprit: Shock, horror! — it was our new best friend, flexbox. The timeline showed that some layouts were taking close to 100 milliseconds; reworking our layouts without flexbox reduced this to 10 milliseconds!

Wilson's comments were about the original (legacy) flexbox that used display: box;. Unfortunately they never got a chance to answer if the newer flexbox (display: flex;) was faster, but over on CSS Tricks, Chris Coyier opened that question.

We asked Ojan Vafai, who wrote much of the implementation in WebKit & Blink, about the newer flexbox model and implementation.

The new flexbox code has a lot fewer multi-pass layout codepaths. You can still hit multi-pass codepaths pretty easily though (e.g. flex-align: stretch is often 2-pass). In general, it should be much faster in the common case, but you can construct a case where it's equally as slow.

That said, if you can get away with it, regular block layout (non-float), will usually be as fast or faster than new flexbox since it's always single-pass. But new flexbox should be faster than using tables or writing custom JS-base layout code.

To see the difference in numbers, I made a head-to-head comparison of old v new syntax.

Old v New Flexbox Benchmark

  • old flexbox vs new flexbox (with fallback)
  • 500 elements per page
  • evaluating page load cost to lay out the elements
  • averaged across 3 runs each
  • measured on desktop. (mobile will be ~10x slower)

Old flexbox: layout costs of ~43.5ms

New flexbox: layout costs of ~18.2ms

Summary: Old is 2.3x slower than new.

What should you do?

When using flexbox, always author for the new stuff: the IE10 tweener syntax and the new updated flexbox that’s in Chrome 21+, Safari 7+, Firefox 22+, Opera (& Opera Mobile) 12.1+, IE 11+, and Blackberry 10+. In many cases you can do a fallback to the legacy flexbox to pick up some older mobile browsers.


  • I also ran the benchmark using display:table-cell and it hit 30ms, right between the two flexbox implementations.
  • The benchmarks above only represent the Blink & WebKit side of things. Due to the time of implementation, flexbox is nearly identical across Safari, Chrome & Android.

Remember: Tools, not rules

What’s more important is optimizing what matters. Always use the timeline to identify your bottlenecks before spending time optimizing one sort of operation.

In fact, we've connected with Wilson and the Financial Times Labs team and, as a result, improved the Chrome DevTools coverage of layout performance tooling. We'll soon be adding the ability to view the relayout boundary of an element, and Layout events in the timeline are loaded with details of the scope, root, and cost of each layout:

Introduction to Custom Filters (aka CSS Shaders)

By Paul Lewis at


Custom Filters, or CSS Shaders as they used to be called, allow you to use the power of WebGL's shaders with your DOM content. Since in the current implementation the shaders used are virtually the same as those in WebGL, you need to take a step back and understand some 3D terminology and a little bit of the graphics pipeline.

I've included a recorded version of a presentation I recently delivered to LondonJS. In the video I step through an overview of the 3D terminology you need to understand, what the different variable types are that you'll encounter, and how you can start playing with Custom Filters today. You should also grab the slides so you can play with the demos yourself.

Introduction to Shaders

I've previously written an introduction to shaders which will give you a good breakdown of what shaders are and how you can use them from a WebGL point of view. If you've never dealt with shaders it's something of a required read before you go much further, because many of the Custom Filters concepts and language hinges on the existing WebGL shader terminology.

So with that said, let's enable Custom Filters and plough on!

Enabling Custom Filters

Custom Filters are available in both Chrome and Canary as well as Chrome for Android. Simply head over to about:flags and search for "CSS Shaders", enable them and restart the browser. Now you're good to go!

The syntax

Custom Filters expands on the set of filters that you can already apply, like blur or sepia, to your DOM elements. Eric Bidelman wrote a great playground tool for those, which you should check out.

To apply a Custom Filter to a DOM element you use the following syntax:

.customShader {

      mix(url(fragment.frag) normal source-atop),

    /* Row, columns - the vertices are made automatically */
    4 5,

    /* We set uniforms; we can't set attributes */
    time 0)

You'll see from this that we declare our vertex and fragment shaders, the number of rows and columns we want our DOM element to get broken down into, and then any uniforms we want to pass through.

A final thing to point out here is that we use the mix() function around the fragment shader with a blend mode (normal), and a composite mode (source-atop). Let's take a look at the fragment shader itself to see why we even need a mix() function.

Pixel Pushing

If you're familiar with WebGL's shaders you'll notice that in Custom Filters things are a little different. For one we don't create the texture(s) that our fragment shader uses to fill in the pixels. Rather the DOM content that has the filter applied gets mapped to a texture automatically, and that means two things:

  1. For security reasons we can't query individual pixel color values of the DOM's texture
  2. We don't (at least in current implementations) set the final pixel color ourselves, i.e. gl_FragColor is off limits. Rather, it's assumed that you will want to render the DOM content, and what you get to do is manipulate its pixels indirectly through css_ColorMatrix and css_MixColor.

That means our Hello World of fragment shaders looks more like this:

void main() {
  css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                         0.0, 1.0, 0.0, 0.0,
                         0.0, 0.0, 1.0, 0.0,
                         0.0, 0.0, 0.0, 1.0);

  css_MixColor = vec4(0.0, 0.0, 0.0, 0.0);

  // umm, where did gl_FragColor go?

Each pixel of the DOM content is multiplied by the css_ColorMatrix, which in the above case does nothing as its the identity matrix and changes none of the RGBA values. If we did want to, say, just keep the red values we would use a css_ColorMatrix like this:

// keep only red and alpha
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                       0.0, 0.0, 0.0, 0.0,
                       0.0, 0.0, 0.0, 0.0,
                       0.0, 0.0, 0.0, 1.0);

You can hopefully see that as you multiply the 4D (RGBA) pixel values by the matrix that you get a manipulated pixel value out of the other side, and in this case one that zeros out the green and blue components.

The css_MixColor is mainly used as a base color that you want to, well, mix in with your DOM content. The mixing is done through the blend modes that you'll be familiar with from art packages: overlay, screen, color dodge, hard light and so on.

There are plenty of ways that these two variables can manipulate the pixels, and included in my presentation is a demo that you can play around with. You should check out the Custom Filters specification to get a better handle on how the blend and composite modes interact.

Vertex Creation

In WebGL we take full responsibility for the creation of our mesh's 3D points, but in Custom Filters all you have to do is specify the number of rows and columns that you want and the browser will automatically break down your DOM content into a bunch of triangles:

Vertex creation
An image being broken down into rows and columns

Each one of those vertices then gets passed through to our vertex shader for manipulation, and that means we can start moving them around in 3D space as we need. It's not long before you can make some pretty crazy effects!

An accordion effect
An image being warped by an accordion effect

Animating with Shaders

Bringing in animations to your shaders is what makes them fun and engaging. To do that you simply use a transition (or animation) in your CSS to update uniform values:

.shader {
  /* transition on the filter property */
  -webkit-transition: -webkit-filter 2500ms ease-out;

  -webkit-filter: custom(
    mix(url(fshader.frag) normal source-atop),
    1 1,
    time 0);

 .shader:hover {
  -webkit-filter: custom(
    mix(url(fshader.frag) normal source-atop),
    1 1,
    time 1);

So the thing to notice in the code above is that time is going to ease from 0 to 1 during the transition. Inside the shader we can declare the uniform time and use whatever its current value is:

uniform float time;

uniform mat4 u_projectionMatrix;
attribute vec4 a_position;

void main() {
  // copy a_position to position - attributes are read only!
  vec4 position = a_position;

  // use our time uniform from the CSS declaration
  position.x += time;

  gl_Position = u_projectionMatrix * position;

Get Playing!

Custom Filters are great fun to play with, and the amazing effects you can create are difficult (and in some cases impossible) without them. It is still early days, and things are changing quite a bit, but adding them will add a little bit of showbiz to your projects, so why not give them a go?

Additional Resources

CSS Background shorthand coming to mobile WebKit browsers

By Pete LePage at

Earlier this year, WebKit updated the behavior of the CSS background shorthand property. With this change, the background shorthand property will reset the background-size to its default value of auto auto if it’s not set in the shorthand declaration. This change brings Chrome and other WebKit browsers in compliance with the specification and matches the behavior of Firefox, Opera and Internet Explorer.

With Chrome for Android moving forward to current revisions on WebKit, this change is now rolling out in Android. Because this was a fix to webkit, sites that tested in multiple browsers are likely not affected.

Old way of doing things

// background-size is reset to auto auto by the background shorthand 
.foo {  
  background-size: 50px 40px;
  background: url(foo.png) no-repeat;

The background shorthand property does not contain any size property and will therefore reset the background-size to its default value of auto auto.

Correct way to specifying image size

// background-size is specified after background
.fooA { background: url(foo.png) no-repeat; background-size: 50px 40px;

// background-size is specified inline after position
.fooB { background: url(foo.png) 0% / 50px 40px no-repeat;

In fooB, specifying a background-size in the background shorthand requires the position (0%) be specified first, followed by a forward slash then the background-size (50px 40px). |

Fixes for existing code

There are several options that will provide an easy fix for this:

  • Use background-image instead of the background shorthand property.
  • Set the background-size last, with a higher specificity than any other CSS rules that set background on that element, and don’t forget to check and :hover rules.
  • Apply the !important property to background-size.
  • Move the sizing information to the background shorthand property.

Bonus: background image offsets

In addition to this change, there’s now greater flexibility in positioning the image within the background. In the past, you could only specify the image position relative from the top left corner, now you can specify an offset from the edges of the container. For example setting background-position: right 5px bottom 5px; the image will be positioned 5px from the right edge and 5px from the bottom. Check out this sample on JSBin

Canvas-driven background images

By Eric Bidelman at

Programmatically animating background images

There are two primary ways people animate background images:

  1. Use CSS sprites to update a background-position in JS .
  2. Hacks with .toDataURL() .

The first works great if you have the image ahead of time, but what if your source needs to be programmatically generated, say, by a <canvas>? The solution to #1 is to use .toDataURL() on the canvas and set the background to the generated URL:

while(1) {
  var url = canvas.toDataURL('image/jpeg'); = 'url(' + url + ')';

There are two problems with this:

  1. data: URLs add a ~33% size overhead to the resulting image.
  2. A ton of DOM touching (

Both of these methods are inefficient: unacceptable for an always-silky-smooth 60fps web app.

Using 2d canvas as a background


Turns out, there's a non-standard API which WebKit has had for years that can take canvas as the source for a background. Sadly however, there's no published spec for this feature.

First, instead of specifying a URL for the back:

.bg {
  background: url(bg.png) no-repeat 50% 50%;

use -webkit-canvas(), referencing a string identifier to a canvas context:

.canvas-bg {
  background: -webkit-canvas(animation) no-repeat 50% 50%;

Next, we need to create the 2d context with a special version of .getContext():

var ctx = document.getCSSCanvasContext('2d', 'animation', 300, 300);

Note: this method is on the document object and not the canvas. The second argument is the same name used in -webkit-canvas().

Further information from Dave Hyatt:

There is only one CSS canvas for a given global identifier per document. When you obtain the drawing context you also specify the size. The canvas will not be cleared as long as you repeatedly request the same size. Specifying a new size is equivalent to the kind of behavior you’d expect if you resized a <canvas> element, and so the canvas buffer will be cleared.

All objects that observe a CSS canvas of the same name are sharing that canvas. This means that (similar to how animated GIFs work), you can do animations and have them happen in lockstep on all the clients of the canvas. Drawing changes will be propagated to all clients automatically.


As seen in the demo, we can reuse requestAnimationFrame() to drive an animation. This is great, because once things are hooked up, the association between CSS and the canvas element is preserved. There's no need to fiddle with the DOM.

Demo not animated in Chrome?

The current stable channel of Chrome (version 23) has, which prevents a requestAnimationFrame() animation from updating the background properly. This has been fixed in Chrome 25 (currently Canary). The demo also should work well in the current Safari.

Performance benefits

We're talking canvas here. Hardware accelerated animations are now fully in play (at least for the browsers this feature works in). And just to reiterate, there's no need to molest the DOM from JS.

Using webgl as a background

Hold on a sec. Does this mean we can power a CSS background using webgl? Of course it does! WebGL is merely a 3d context for canvas. Just swap in "experimental-webgl" instead of "2d", and voila.

var gl = document.getCSSCanvasContext('experimental-webgl', 'animation', 300, 150);

Here's a proof of concept that contains a div with it's background drawn using vertext and fragment shaders: DEMO

Other approaches

It's worth noting that Mozilla has had -moz-element() (MDN) for quite some time. This is part of the CSS Image Values and Replaced Content Module Level 4 spec and allows you to create an image generated from arbitrary HTML: videos, canvas, DOM content, name it. However, there are security concerns with having full access to snapshot images of the DOM. This is primarily why other browsers have not adopted said feature.

Interactive Globe with CSS shaders & Google Maps

By Paul Irish at

Recently, I have read news on Webmonkey that Adobe’s CSS Shaders proposal, which will bring high-quality cinematic effects to the web through some new CSS tools, has been accepted by the W3C. If you haven't seen it yet, watch the video below:

Google Chrome's latest Canary added support for CSS shaders, so I decided to experiment with them.

In this experiment, I used custom vertex shader (spherify.vs) and fragment shader (spherify.fs) to create a globe with Google Maps.

.globe {
  width: 550px;
  height: 550px;
  border: 0;
  -webkit-filter: contrast(1.4) custom(url(shaders/spherify.vs) mix(url(shaders/spherify.fs) multiply source-atop),
    50 50 border-box,
    amount 1,
    sphereRadius 0.5,
    sphereAxis -0.41 1 0.19,
    sphereRotation 43.5,
    ambientLight 0.15,
    lightPosition 1 0.87 0.25,
    lightColor 1 1 1 1,
    transform perspective(500));

Here, we're applying a vertex shader (spherify.vs) which will operate on a mesh that has 50 lines and 50 columns (50 50 border-box). Feel free to read the source of the vertex shader: spherify.vs. It's written in GLSL but you can probably follow along.

The mix() function provides basic functionalities for color manipulation like blending and alpha compositing on a fragment shader.

We can change the shere's radius, axis, rotation right in the CSS. In this example we set the value of the sphereRadius: 0.5 and it gives original sphere size.

Enjoy the demo!

Below is a video of the effect. If you've got shaders enabled you can play with the real thing right below!

If you just see a flat google maps above, you can enable it with the instructions below

Browsers support: CSS shaders

This is currently cutting-edge, so it's only available in the latest Google Chrome Canary and WebKit nightly. To enjoy the full experience you'll need to turn a few knobs.

Chrome Canary steps:

  • Type about:flags in the browser's navigation bar
  • Find "Enable CSS Shaders". Enable it
  • Relaunch the browser

WebKit nightly steps

  • Download and install WebKit nightly for Mac OSX
  • Open the browser's preferences panel. Go to Advanced tab and tick to show Develop > Enable WebGL menu in the menu bar.
  • In the browser's menu bar select Develop

Stacking Changes Coming to position:fixed elements

By Tom Wiltzius at

In Chrome 22 the layout behavior of position:fixed elements is slightly different than previous versions. All position:fixed elements now form new stacking contexts. This will change the stacking order of some pages, which has the potential to break page layouts. The new behavior matches the behavior of WebKit browsers on mobile devices (iOS Safari and Chrome for Android).

Stacking Whats?

Everyone knows and loves the z-index for determining depth ordering of elements on a page. Not all z-indexes are created equal, however: an element's z-index only determines its ordering relative to other elements in the same stacking context. Most elements on a page are in a single, root stacking context, but absolutely or relatively positioned elements with non-auto z-index values form their own stacking contexts (that is, all of their children will be z-ordered within the parent and not be interleaved with content from outside the parent). As of Chrome 22, position:fixed elements will also create their own stacking contexts.

For a general overview of stacking contexts this MDN article is a great read.

Compare position:fixed to the new position:sticky attribute: for reference, position:sticky always creates a new stacking context.


Mobile browsers (Mobile Safari, Android browser, Qt-based browsers) put position:fixed elements in their own stacking contexts and have for some time (since iOS5, Android Gingerbread, etc) because it allows for certain scrolling optimizations, making web pages much more responsive to touch. The change is being brought to desktop for three reasons:

  1. Having different rendering behavior on "mobile" and "desktop" browsers is a stumbling block for web authors; CSS should work the same everywhere when possible.
  2. With tablets it isn’t clear which of the "mobile" or "desktop" stacking context creation algorithms is more appropriate.
  3. Bringing the scrolling performance optimizations from mobile to desktop is good for both users and authors.

Specifics of the Change

Here's an example showing the different layout behaviors:

With the change, both versions will render like the right-hand version.

In this example, the green box has a z-index: 1, the pink box has a z-index: 3, and the orange box has a z-index: 2. The blue box is an ancestor of the orange box, and has position:fixed.

If the blue box gets its own stacking context, the orange box's z-index is computed relative to the blue box’s stacking context. Since the blue box has a z-index of auto, giving it a stacking level of zero in the root stacking context, this means the orange box ends up behind the green and pink boxes, which have z-indexes of 1 and 3 (respectively) in the root context.

If the blue box does not get its own stacking context, the orange box's z-index is computed relative to the root stacking context (along with the green and pink boxes). Hence, the orange box ends up interleaved with the pink and green boxes.

For more detail about criteria for stacking context creation (and how stacking contexts behave in general), again refer to this MDN article. In the example the right-hand side version always gave the blue box its own stacking context because its opacity is less than 1. The behavior change being made effectively adds another criterion for creating a separate stacking context, namely an element being position:fixed.

Testing & The Future

To test if your page is going to change, go to Chrome's about:flags and turn on/off "fixed position elements create stacking contexts". If your layout behaves the same in both cases, you're set. If not, make sure it looks acceptable to you with that flag enabled, as that will be the default in Chrome 22.

This change removes one capability - the ability to interleave content within a position:fixed subtree with non-scrolling content from outside. It's unlikely that any web developers are doing this on purpose, and the same effect can be created by giving multiple position:fixed elements the different portions of DOM. As an example, consider these two examples:

This page attempts to take two child divs (overlayA and overlayB) of a position:fixed element and place one above a separate content div and one below that same separate content div. It’s now impossible to do this because the position:fixed element is its own stacking context, and it (along with all of its children) will be either entirely above or entirely below the content div. Note that this example works in Chrome 21 and earlier but no longer in Chrome 22.

To fix this, the two overlays can be broken up into their own position:fixed elements. Each is its own stacking context, one of which can go above the content div and one of which can go below the content div. See the fixed example, which works in Chrome 21 and 22:

Credit for the genesis of this example goes to the inimitable hixie.

Chrome is the first desktop browser to cause position:fixed elements to create their own stacking contexts. The relevant standard is the CSS z-index specification (see e.g. There's not yet consensus over what to do about the difference between mobile and desktop browsers here, but given the confusion of having two different behaviors on mobile and desktop, Chrome has chosen to move to this single behavior on both platforms for the time being.

Updated Oct 1, 2012: The original version of this article suggested that the CSS z-index specification had already been changed to reflect the new behavior of position: fixed elements. This is inaccurate; it has been discussed on the www-style list but as of yet no change has been taken into the spec.

Stick your landings! position: sticky lands in WebKit

By Eric Bidelman at

position: sticky is a new way to position elements and is conceptually similar to position: fixed. The difference is that an element with position: sticky behaves like position: relative within its parent, until a given offset threshold is met in the viewport.

Use cases

Paraphrasing from Edward O’Connor's original proposal of this feature:

Many web sites have elements that alternate between being in-flow and having position: fixed, depending on the user's scroll position. This is often done for elements in a sidebar that the page author wants to be always visible as the user scrolls, but which slot into a space on the page when scrolled to the top. Good examples are (the "Top Stories" sidebar) and (search results map).

Introducing sticky positioning


By simply adding position: sticky (vendor prefixed), we can tell an element to be position: relative until the user scrolls the item (or its parent) to be 15px from the top:

.sticky {
  position: -webkit-sticky;
  position: -moz-sticky;
  position: -ms-sticky;
  position: -o-sticky;
  top: 15px;

At top: 15px, the element becomes fixed.

To illustrate this feature in a practical setting, I've put together a DEMO which sticks blog titles as you scroll.

Old approach: scroll events

Until now, to achieve the sticky effect, sites setup scroll event listeners in JS. We actually use this technique as well on html5rocks tutorials. On screens smaller than 1200px, our table of contents sidebar changes to position: fixed after a certain amount of scrolling.

Here's the (now old way) to have a header that sticks to the top of the viewport when the user scrolls down, and falls back into place when the user scrolls up:

.sticky {
  position: fixed;
  top: 0;
.header {
  width: 100%;
  background: #F6D565;
  padding: 25px 0;

<div class="header"></div>

var header = document.querySelector('.header');
var origOffsetY = header.offsetTop;

function onScroll(e) {
  window.scrollY >= origOffsetY ? header.classList.add('sticky') :

document.addEventListener('scroll', onScroll);

Try it:

This is easy enough, but this model quickly breaks down if you want to do this for a bunch of DOM nodes, say, every <h1> title of a blog as the user scrolls.

Why JS is not ideal

In general, scroll handlers are never a good idea. Folks tend to do too much work and wonder why their UI is janky.

Something else to consider is that more and more browsers are implementing hardware accelerated scrolling to improve performance. The problem with this is that on JS scroll handlers are in play, browsers may fall back into a slower (software) mode. Now we're no longer running on the GPU. Instead, we're back on the CPU. The result? User's perceive more jank when scrolling your page.

Thus, it makes a lot of sense to have such feature be declarative in CSS.


Unfortunately, there isn't a spec for this one. It was proposed on www-style back in June and just landed in WebKit. That means there's no good documentation to point to. One thing to note however, according to this bug, if both left and right are specified, left wins. Likewise, if top and bottom are used at the same time, top wins.

Support right now is Chrome 23.0.1247.0+ (current Canary) and WebKit nightly.

Writing a flippable book using CSS Regions and 3D transforms

By Ilmari Heikkinen at

So. The day has come. You have finally grown bored of long scrolls of text and are looking for a new format. Something elegant. Something compact. Something that takes the long scroll, cuts it into neat little rectangles and binds them together. I call this invention the “book”.

With the power of CSS regions (CanIUse, go to chrome://flags and enable CSS Regions) and CSS 3D transformations, cutting-edge book technology is finally available on modern browsers. All you need is a few lines of JavaScript and a whole lot of CSS.

Let’s start off by defining our book structure. The book consists of pages and the pages consists of two sides. The sides contain the book content:

<div class="book">
        <div> <!-- first page -->
          <div> <!-- front cover -->
            <h1>My Fancy Book</h1>
          <div> <!-- backside of cover -->
            <h1>By Me I. Myself</h1>
            <h2>2012 Bogus HTML Publishing Ltd</h2>
        <!-- content pages -->        
          <!-- front side of page -->
          <div class="book-pages"></div>
          <!-- back side of page -->
          <div class="book-pages"></div>
          <div class="book-pages"></div>
          <div class="book-pages"></div>
          <div class="book-pages"></div>
          <div class="book-pages"></div>

We’re going to use CSS regions to flow the book text into the book pages. But first we need the book text.

<span id="book-content">
  blah blah blah ...

Now that we have written our book, let’s define the flow CSS. I’m using the + character as a vendor prefix placeholder, replace it with -webkit- for WebKit browsers, -moz- for Firefox, and so on:

#book-content {
  +flow-into: book-text-flow;
.book-pages {
  +flow-from: book-text-flow;

Now the content from the #book-content span will go into the .book-pages divs instead. This is a rather poor book though. For a more bookish book we must embark on a quest. Our journey shall lead over the rainbow bridge of CSS transforms to the clockwork kingdom of JavaScript. In the halls of the mechanist fairylords we shall unleash epic transition magicks and obtain the fabled three keys that control the overworld interface.

The guardian of the rainbow bridge imparts on us the wisdom of stylish structural selectors so that we may turn our HTML book structure into a more book-shaped form:

html {
  width: 100%;
  height: 100%;
body {
  /* The entire body is clickable area. Let the visitor know that. */
  cursor: pointer;
  width: 100%;
  height: 100%;
  /* Set the perspective transform for the page so that our book looks 3D. */
  +perspective: 800px;
  /* Use 3D for body, the book itself and the page containers. */
  +transform-style: preserve-3d;
.book {
  +transform-style: preserve-3d;
  position: absolute;
/* Page containers, contain the two sides of the page as children. */
.book > div {
  +transform-style: preserve-3d;
  position: absolute;
/* Both sides of a page. These are flat inside the page container, so no preserve-3d. */
.book > div > div {
  /* Fake some lighting with a gradient. */
  background: +linear-gradient(-45deg, #ffffff 0%, #e5e5e5 100%);
  width: 600px;
  height: 400px;
  overflow: hidden;
  /* Pad the page text a bit. */
  padding: 30px;
  padding-bottom: 80px;
/* Front of a page */
.book > div > div:first-child {
  /* The front side of a page should be slightly above the back of the page. */
  +transform: translate3d(0px, 0px, 0.02px);
  /* Add some extra padding for the gutter. */
  padding-left: 40px;
  /* Stylish border in the gutter for visual effect. */
  border-left: 2px solid #000;
/* Back of a page */
.book > div > div:last-child {
  /* The back side of a page is flipped. */
  +transform: rotateY(180deg);
  padding-right: 40px;
  border-right: 2px solid #000;
/* Front cover of the book */
.book > div:first-child > div:first-child {
  /* The covers have a different color. */
  background: +linear-gradient(-45deg, #8c9ccc 0%, #080f40 100%);
  /* Put a border around the cover to make it cover the pages. */
  border: 2px solid #000;
  /* And center the cover. */
  margin-left: -1px;
  margin-top: -1px;
/* Back cover of the book */
.book > div:last-child > div:last-child {
  background: +linear-gradient(-45deg, #8c9ccc 0%, #080f40 100%);
  border: 2px solid #000;
  margin-left: -1px;
  margin-top: -1px;

In thus creating a somewhat paper-shaped style for our HTML, we arrive at the trillion-geared gates of the JavaScript kingdom. To pass through the gate, we must turn our flat book into a proper volume. To add some volume to the book, we offset each page slightly on the z-axis.

(function() {
var books = document.querySelectorAll('.book');
for (var i = 0; i < books.length; i++) {
  var book = books[i];
  var pages = book.childNodes;
  for (var j = 0; j < pages.length; j++) {
    if (pages[j].tagName == "DIV") {
      setTransform(pages[j], 'translate3d(0px, 0px, ' + (-j) + 'px)');

Casting transition magic to impress the fairylords is not the most difficult of invocations. Yet, the results make the pages of our book animate their turning in a smooth fashion.

.book > div {
  +transition: 1s ease-in-out;

Finally, to make the pages actually turn, we need to bind the events themselves to our cause.

	// Get all the pages.
	var pages = document.querySelectorAll('.book > div');
	var currentPage = 0;
	// Go to previous page when clicking on left side of window.
	// Go to the next page when clicking on the right side.
	window.onclick = function(ev) {
	  if (ev.clientX < window.innerWidth/2) {
	  } else {
	var previousPage = function() {
	  if (currentPage > 0) {
            // Rotate the page to closed position and move it to its place in the closed page stack.
	    setTransform(pages[currentPage], 'translate3d(0px,0px,' + (-currentPage) + 'px) rotateY(0deg)');
	var nextPage = function() {
	  if (currentPage < pages.length) {
            // Rotate the page to open position and move it to its place in the opened stack.
	    setTransform(pages[currentPage], 'translate3d(0px,0px,' + currentPage + 'px) rotateY(-150deg)');

With that, we have acquired the “book” technology and can evacuate the overworld crystal towers and leave behind their blinding glare and the fierce nuclear fires of Achenar, the great blue star of the overworld nexus. We triumphantly return to our homes, brandishing our books high above our heads, ready for the inevitable cascade of parades and celebrations in our honor.

You can see an example online here and get the full source for the examples. If you don’t have CSS Regions in your browser, the example will look quite broken. In which case you can try this example instead.