Blurry Transparent Header Effect from iOS7 in CSS
Published by Guest Author
The following is a guest post by Fabrice Weinberg. Fabrice is a regular contributor around here. He's always looking forward to the latest technologies and possibilities. Here on the outset of iOS7, he looks into recreating a particular effect that you might not guess is even possible on the web. It's tricky, uses bleeding edge features, but doable!
Let's start with the backstory. @simurai asked a while ago if it where possible to do a blur effect on arbitrary DOM nodes to simulate the iOS7 Translucent Effect on the Control Center (you can find his Pen here).
I won't explain exactly how to build this effect for the Control Center but rather focus on how to build something similar to the blurred header effect you can find in iMessage and many other apps in iOS7. But the tricks you will learn will make it possible to build the Control Center effect too.
This solution makes heavy use of CSS Regions which is still a working draft but thanks to Adobe it is implemented in WebKit/Blink today.
With the release of iOS7, Mobile Safari is the first browser to enable this feature by default (still
-webkit- prefixed). In Google Chrome 29 you just need to enable
experimental-webkit-features. in Google Chrome 30+ it is experimental-web-platform-features under
Quick introduction to CSS Regions: it is all about splitting the structure of the page from the content. There are two new CSS properties
flow-into: named-region; and
flow-from: named-region; these two are used to defined a so called
named-region. You can think of it as a variable which will hold arbitrary content.
The element with
flow-into on it is like a reservoir of content. That content "flows" into other elements with
flow-onto on them.
A simple example:
Assuming your browser supports regions, you'll be able to see the structure is separated from the content. Using
flow-into: content the content from the div with the class
.main-content is flowed though both divs with the class
For a more in depth look at CSS Regions read the great article by Arno Gourdol CSS3 regions: Rich page layout with HTML and CSS3
Let's begin by defining the HTML structure. It is quite simple. We need some sort of a header and a container for the messages. Because of the use of CSS Regions, another container is needed to hold the content which will flow though the header and message container.
There is CSS here to blur the
Like in the first example, everything that doesn't fit in the
.header flows though the
That is not the behavior we are looking for. We want to have the whole content in the
.content-container and only blur content that is "hidden" by the
.header. Let's fix that.
Using CSS Regions to "beam" the DOM
This part is kinda hacky because the behavior of putting
overflow-y: auto on an element that gets the CSS property
flow-into: named-region; isn't described in the spec and thus dependent on the implementation.
CSS regions allow us to define the flow of arbitrary DOM nodes and even let us add
overflow-y: auto to make the content scrollable. Additionally, there is the need of a defined height.
Let's use the sample from above, adding a fixed height and
overflow-y: scroll to the
That doesn't look that good yet. Scrolling is working but the
.header stays empty.
The fun part comes now, we are about to "beam" the content from
.main-content to the
.header element. This can be done with CSS3 and the ability to transform the whole content layer of the
.main-content by using
transform: translateY() to move the
.main-content upwards by the amount of the
That looks better! But still something looks wrong. There is always some content hidden by the
.header now. To fix this we just have to add the height of the
padding-top to the
.main-content to accommodate for the space taken.
That's it. This is the core technique used to achieve the iOS7 translucent/blurry effect. Based on this technique, I built this little playground where you can customize the header and even add a tint color.
Let's have a look at the render performance of this effect:
Above you see a timeline taken while scrolling up and down, you can see that nearly half the time we have per frame (16.66ms) is used for painting. Before we look into that I want to tell you about a great new feature in the Chrome DevTools called Layers. It's a new tab in the DevTools where you can see what elements Chrome promotes to its own layer in a document. It is still experimental so you have to enable the
devtools-experiments flag in
about:flags. Be sure to restart Chrome afterwards. Then to activate the Layers experiment open the dev tools click on the wrench icon in the lower right corner (or just hit
?) to open the settings.
Using The Layers Tab
Above you see the Layers tab open. Chrome created just one layer for the whole document, that means that any change on the page will trigger a repaint of the whole document. That is expensive! You may have noticed the checkbox in the upper left corner. It is used to apply
transform: translateZ(0) on the
.header element. You can find the Pen here. The
transform: translateZ(0) hack is used to promote an element to its own layer on the GPU.
As you can see there is an additional layer for the
div.header now. Let's do another timeline, now using the
transform: translateZ(0) hack:
This looks so much better. Paint costs are only approximately 1/10th of the overall time per frame now. With the new layers tab it is now much easy to see where Chrome created layers and to see potential performance bottlenecks.
Wrapping It Up
It's fun to see that this is possible today in WebKit/Blink. But (there is always is a but) this technique is not ready for production. Though it's working quite well on the desktop it's not working on iOS7 at the moment, it's slow and scrolling is not working at all.