Skip to content

Recreating Tumblr’s “Updating…” page using pure CSS3

Each and every day we’re witnessing more and more HTML/CSS3 tutorials, experiments and projects; new standards are being pushed not just by web developers, but by prominent and popular services, such as Google, Apple and lately even Microsoft.

Using CSS3 (I’m aiming at gradient backgrounds, rounded borders, text and box shadows here) could have a big (or at least significant) effect on bandwidth usage. Background images used purely for design are all costly, bandwidth consuming. If we’d use plain CSS to handle all the graphics, we could save at least a few KB per page. With a lot of visits, this number could turn out to be quite significant.

updating original e1287683922184

And this is why I’d like to overview one of most popular services on web today: Tumblr. It actually has 1 billion pageviews monthly (view the stats). Tumblr is unavailable quite a lot, and it’s having constant problems with downtimes (similar to Twitter’s), and this makes him a perfect candidate for optimization.

We’ll examine the infamous “We’ll be back shortly” (Tumblr’s Fail Whale) page and try to optimise it using CSS3 techniques. I’ll optimise only CSS; HTML markup will remain (more or less) the same. For browsers that don’t support all rules necessary to display the exact page using pure CSS3, we’ll use images fallback (similar to what site uses now).

I promise you, if Tumblr employed optimisations I’ll show here, there wouldn’t be any downtime’s, and furthermore, Tumblr would probably save a trillions and trillions of dollars on bandwidth. (You didn’t take this last bit seriously, did you?)

Let’s start by overviewing the current “We’ll be back shortly” page. Please note that I’m using “Updating” version of the page that they had while rolling out new queues features. On other versions the text changes a little, but everything else is the same.

The markup

I noticed a few interesting things there. First off, I’ve ran the page trough W3C validators (both HTML and CSS). CSS passes with a flying colors – not a single error or warning. Neat. HTML is not bad either; except that it returns one error:

You may have neglected to close an element, or perhaps you meant to “self-close” an element, that is, ending it with “/>” instead of “>”.

They’ve kind of missed that. No biggy. But simple backslash will fix it:

[code lang=”html”] [/code]

I’ve also replaced few class selectors with id selectors; best practice is to use ID’s whenever possible; it’s faster in browser renderings (not that you should be concerned with that), and if you use JavaScript, it is more efficient and faster to get elements by ID’s then by class names.

The main thing that’s poking my eyes is the use of non breaking spaces ( ) to indent “Updating” word. Why would you use that? We have a perfectly good text-indent property that’s supported by all imaginable browsers; and yes, even by IE6.

The problem is that they’re setting the text-align property of the flip container to center; but then they overwrite it with inline styles to left. I’m not gonna even start with the rantings about the use of inline styles; you already have a external CSS and a class on the element you’re trying to style, please use it. Even if this means that you’ll have to do a slight change when you replace text in HTML (I think that’s the reason they did this).

I would replace .green and .red classes with more generic names. It’s bad practice to name your classes by style. What if a designer decides to change the colors? You’d have a blue element with .green class; or you’d have to change HTML as well. It probably isn’t a big deal, but it’s a good practice nonetheless. These elements are related to site status, so I’ve changed class names to .online and .offline.

It would also be a smart decision to use a heading on the page; all we have now is semantically just plain text. But that’s not really important for this temporary page, so I won’t be adding it.

This is the complete html I’m using here.

[code lang=”html”]

We’ll be back shortly


This feature is temporarily unavailable while we rollout software updates.

Please check back shortly!


CSS3 goodness

Don’t be too harsh on me with the following part. I wanted to recreate the original effect as closely as possible and ended up with the following combination (It’s probably possible to create a similar effect with less rules).

Here’s the complete CSS. You’ll find a few comments in there, but everything should be self explanatory. I’ve used multiple backgrounds to recreate the original effect; rgba, linear and radial backgrounds, box-shadows and as a cherry on top I’ve added a simple glow animation (you’ll see this only in Webkit browsers).

[code lang=”css”]
body, html {
font-family: Arial, Helvetica, Arial, sans-serif;

#container {
width: 709px;
height: 256px;
margin:-128px 0 0 -348px;
padding: 0 0 0 0px;
position: absolute;
top: 50%; left: 50%;

background: url(images/status_flip_container.png) 79px 0px no-repeat;
background: -moz-linear-gradient(90deg, rgba(0,0,0,0.0), rgba(0,0,0,0.0) );
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0.0)), to(rgba(0,0,0,0)));

margin: 20px 0 0; float: left;
width: 584px; height: 112px;
position:relative; overflow:hidden;

background: none;
background: -moz-linear-gradient(90deg, rgba(0,0,0,0.8), rgba(0,0,0,0.5) ),
-moz-linear-gradient(90deg, rgba(125,125,125,0.5), rgba(125,125,125,0.8) ),
-moz-radial-gradient(top 90deg, ellipse closest-side, rgba(204,204,204,0.1) 30%, rgba(0,0,0,0.5) 120% ),
-moz-linear-gradient(90deg, rgba(70,70,70,0.5), rgba(0,0,0,1) );
background: -webkit-gradient(linear, left top, left bottom, from(rgba(70,70,70,0.4)), to(rgba(0,0,0,0.4))),
-webkit-gradient(radial, center -400, 50, center -480, 682, from(rgba(204,204,204,0.4)), to(rgba(70,70,70,0.2))),
-webkit-gradient(radial, left center, 150, left center, 622, from(rgba(70,70,70,0.1)), to(rgba(0,0,0,0))),
-webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(125,125,125,0.3)), color-stop(0.01, rgba(0,0,0,0.4)), color-stop(0.03, rgba(0,0,0,0.4)) ),
-webkit-gradient(linear, left top, left bottom, color-stop(0.5, rgba(0,0,0,0.5)), color-stop(1, rgba(0,0,0,0.9)) );

color: #dadada;
font-size: 54px; font-weight: bold;
text-align: left; line-height: 104px;
text-indent: 46px; letter-spacing: -2px;

border-radius: 15px;
-moz-border-radius: 15px;
-webkit-box-shadow: 0 -1px 0 rgba(24,24,24,1), 0 0 35px rgba(121,121,121,0.2);
-moz-box-shadow: 0 -1px 0 rgba(24,24,24,1), 0 0 35px rgba(121,121,121,0.2);

border-top: 2px solid rgba(24,24,24,0.3);
border-top: 1px solid rgba(66,66,66,1);
border-bottom: 1px solid rgba(70,70,70,1);
border-left: 1px solid rgba(70,70,70,0.6);
border-right: 1px solid rgba(70,70,70,0.6);

text-shadow: 0 0 2px rgba(0,0,0,0.5);

width:96px; height:115px; float: left;
background: url(images/status_led_sprite.png) 0 -70px no-repeat;
background: -moz-linear-gradient(90deg, rgba(0,0,0,0.0), rgba(0,0,0,0.0) );
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0.0)), to(rgba(0,0,0,0)));

#leds.on{ background-position: 0 44px; }

.online, .offline{
margin: 57px 13px 0 13px;
width: 11px; height: 41px;
float: left; display: inline;

background: -moz-linear-gradient(-90deg, rgba(85,92,79,0.9), rgba(69,76,62,1) 1%, rgba(42,49,34,1) 97%, rgba(15,15,15,1) 100% );
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(85,92,79,0.9)), color-stop(0.04, rgba(69,76,62,1)), color-stop(0.96, rgba(42,49,34,1)), color-stop(1, rgba(15,15,15,1)) );
border-top: 1px solid #0f0f0f;
border-right: 1px solid #0f0f0f;
border-left: 1px solid #0f0f0f;
border-bottom: 1px solid #4e4e4e;
-webkit-box-shadow: 0 -1px 10px rgba(122,186,53, 0.5), 0 -1px 20px rgba(122,186,53, 0.5);
-moz-box-shadow: 0 -1px 10px rgba(122,186,53, 0.5), 0 -1px 20px rgba(122,186,53, 0.5);
overflow: visible;
background: -moz-linear-gradient(-90deg, rgba(156,204,105,0.9), rgba(122,186,53,1) 1%, rgba(95,159,26,1) 98%, rgba(40,40,40,0.5) 100% );
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(156,204,105,0.9)), color-stop(0.04, rgba(122,186,53,1)), color-stop(0.96, rgba(95,159,26,1)), color-stop(1, rgba(0,0,0,1)) );
border-top: 1px solid rgba(44,66,20,1);
border-right: 1px solid rgba(42,63,20,0.8);
border-left: 1px solid rgba(42,63,20,0.8);
border-bottom: 1px solid rgba(80,99,60,1);

background: -moz-linear-gradient(-90deg, rgba(113,91,91,0.9), rgba(100,64,64,1) 1%, rgba(72,37,37,1) 98%, rgba(15,15,15,1) 100% );
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(113,91,91,0.9)), color-stop(0.04, rgba(100,64,64,1)), color-stop(0.96, rgba(72,37,37,1)), color-stop(1, rgba(15,15,15,1)) );
border-top: 1px solid #0f0f0f;
border-right: 1px solid #0f0f0f;
border-left: 1px solid #0f0f0f;
border-bottom: 1px solid #4e4e4e;
-moz-box-shadow: 0 -1px 10px rgba(199,58,58, 0.5), 0 -1px 20px rgba(199,58,58, 1);
-webkit-box-shadow: 0 -1px 10px rgba(199,58,58, 0.5), 0 -1px 20px rgba(199,58,58, 1);
background: -moz-linear-gradient(-90deg, rgba(226,120,120,0.9), rgba(216,74,74,1) 1%, rgba(188,47,47,1) 98%, rgba(40,40,40,0.5) );
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(226,120,120,0.9)), color-stop(0.04, rgba(216,74,74,1)), color-stop(0.96, rgba(188,47,47,1)), color-stop(1, rgba(0,0,0,1)) );

border-top: 1px solid rgba(97,30,30,1);
border-right: 1px solid rgba(83,27,27,0.8);
border-left: 1px solid rgba(83,27,27,0.8);
border-bottom: 1px solid rgba(125,69,69,1);

-webkit-animation-name: blink;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: normal;
-webkit-animation-timing-function: ease;

@-webkit-keyframes blink {
from {
-webkit-box-shadow: 0 -1px 10px rgba(199,58,58, 0.5), 0 -1px 20px rgba(199,58,58, 1);
-webkit-box-shadow: 0 -1px 10px rgba(199,58,58, 0.5), 0 -1px 20px rgba(199,58,58, 0.5);
to {
-webkit-box-shadow: 0 -1px 10px rgba(199,58,58, 0.5), 0 -1px 20px rgba(199,58,58, 1);

width: 100%; height: 2px;
position: absolute; top: 55px; left: 0;
border-bottom: 1px solid rgb(54,54,54);
background: rgb(24,24,24);

margin: 0 0 0 113px; padding: 4px 0 0;
clear: both;
color: #949494;
#sorry p{ margin: 16px 0 21px; }

width: 3px; height: 10px; overflow: hidden;
background: #565656;
background: -moz-linear-gradient(-90deg, rgba(121,121,121,1), rgba(86,86,86,1) );
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(121,121,121,1)), color-stop(1, rgba(86,86,86,1)) );
position: absolute;
top: -3px;
border-radius: 2px;
-moz-border-radius: 2px;

.link.left{ left:11px; }
.link.right{ right:11px; }

If you want additional info on CSS3 properties I’ve used, there’s plenty of resources on the web.

The result. Can you figure out what part is done with pure css?

Original page and pure CSS3 page comparison
Original page and pure CSS3 page comparison

Bottom half is CSS3 version, that’s right. Nasty borders gave it away, didn’t they?

Fallback’s for older browsers.

Code we just created will work only in latest Firefox and Webkit browsers, but we don’t want to let all the other users hanging. We’ll show them the page with background images (the original page). Best part is that I didn’t use any browser-specific hacks or checks, any JavaScript or anything like that. Everything is done in CSS. I set up regular CSS2 rule that all browsers can render and then add CSS3 styles to overwrite them.

[code lang=”css”]
background:url(images/status_led_sprite.png) 0 -70px; /*Fallback*/
background:-moz-linear-gradient(90deg, rgba(0,0,0,0.0), rgba(0,0,0,0.0) );
background:-webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0.0)), to(rgba(0,0,0,0)));

Mozilla and Webkit browsers will use the linear gradient rule, and other browsers, that don’t support this will simply ignore rules they can’t render.

The results.

We’ve managed to save 2KB. Original file with images was 9KB, and pure CSS3 version is only 7KB. A few KB may not seem like much, but if you have 1 billion pageviews per month, this could turn out to be a decent number.

Size comparison

But there is a catch. We increased the document size for browsers that don’t support CSS3. They’ll have to download all images and a new, bigger CSS. And this means that the real number of KB saved depends on percentage of browsers the visitors use. If Safari, Firefox and Chrome (latest versions) don’t represent a greatest part, we didn’t actually saved bandwidth usage, but we’ve increased it.


  • Faster loading times.
  • Less bandwidth usage.
  • Hype generation?


  • It takes more time to create the effect using CSS.
  • Rounded borders are a bit rough on the edges.
  • Pins are lacking fine touches – due to a border-radius restriction you can’t set border radius on divs smaller than 4px.
  • CSS validation fails miserably.
  • CSS3 specification isn’t done yet and it could change; this would mean that developer has to revisit and rewrite the code.
  • Double work. You still have to create images version for browsers that don’t support all CSS3 rules.


This analysis is completely useless (correct me if I’m wrong), but it was fun and challenging to make. If nothing else, it shows one practical scenario of progressive enhancement using CSS3 techniques.

For this to be worth implementing, the site traffic should be really really high, and even then it would be questionable if it’s worth hassling and doing all the extra work.

What do you think?

13 thoughts on “Recreating Tumblr’s “Updating…” page using pure CSS3”

  1. Actually you have saved nothing as you’re still serving the image and it gets transmitted. So you have 9kb img + 5kb code.

    Nevertheless, that’s fucking fascinating.

    1. Thank you very much for the comment.

      I’ve tested this again, just to be sure. You’re right and wrong at the same time. 🙂

      Here’s a quick test as a proof of concept.
      Create a simple html file and into a css paste both following rules:

      body{ background:url(gigantic.bmp); }
      body{ background:url(small.jpg); }

      Create two test images. Extra big (.bmp should do the trick), and extra small.

      Now test it and you’ll find that browsers are smart enough to figure out that second rule overwrites the first one, so the first rule is ignored. As a result, big image isn’t downloaded.

      In a tutorial case, images actually gets served only in browsers that, as I explained, don’t support the modern CSS3 rules. So yes, IE users, for example, will get the images and the extra code. But Chrome 8 users, or Firefox 3.6 users, will not get images, and images will not be transmitted.

      That’s why I said that the actual savings depend on modern/non-modern browsers ratio.

  2. Pingback: 30 примеров того, что можно сделать при помощи CSS3

  3. Just to add to the artice: Webkit (Safari) decided to adapt to firefox gradient notation…

    So you would have to stack up stuff like this (skipping the IE filtering though):

    background:-webkit-gradient(linear,left top,left bottom,from(#222),to(#444));

    This is a working example, viewable live at my website btw.

    1. Mike, yes, Webkit has adapted Firefox’s gradient notation. I think that this is a good thing. It’s cleaner, easier, nicer.
      This article was written prior to this.

      Thank you for noting this and for sharing the code.

  4. You can tell the bottom image is the CSS3 page, because the rounded corners on the bottom look wierd

    1. Yes, they’re not perfect, but the question is are they good enough?

      And what is the main concern for the average user? That the page will be loaded a half of a second earlier, or that a rounded corners look a little bit weird? And will the average user even notice this?

      Anyhow, I appreciate your comment, this is a very good point.

Comments are closed.