Update: Good news, everyone! A proper proposal for this kind of thing is already on its way. :)
While CSS sprites offer nice performance benefits (less connections/overhead), they are troublesome in every respect. Putting lots of tiny images with different dimensions into one bigger image is fiddly and very hard – np-hard in fact. But that's the smallest issue. It doesn't need to be perfect after all. Deflate will happily squish all that extraneous white-space to virtually nothing.
One of the real problems is CSS. It just isn't flexible enough to let you do everything you might want to do with your sprite sheet. For example repeating parts of the image isn't possible. And if you want to display a sub region of an image in the upper right of some element, it only works if that sub region is in the lower left of the sprite sheet. So, the best thing you can do is to use elements in the size of the sub region and use background-position to slide the image around, but that usually means extra markup for something that should be very simple.
But it doesn't stop there. The real nightmare is maintenance. A year or two and several iterations later it becomes very hard to figure out which parts of the sheet are used. Where can you add one? Which one can be removed? How many places do you have to fix if you move this column a bit up? It all becomes a mess of fuzzy uncertainty.
CSS sprites are a huge time sink. They waste my time, they waste your time, and they also waste the time of millions of other front-end developers. It's about time to do something about it.
I thought about the issue for a while and using archives was the first thing that came to mind. Here are the key features in a nutshell:
Just update your files, archive it anew, and you're good to go. It really can't get any easier than that. The automation part is also very intriguing. The content management system can for example check if one of the CSS files was changed and if so, aggregate them, minify them, and then create a list of images for the archive. It's very straightforward and requires very little processing power.
The fun doesn't stop there though. You can put everything into the archive! Favicon, scripts, style sheets, and all your (layout) images. Well, you can even add the document itself, but that won't be practical for most use cases and it also comes with some additional implications. So, we'll ignore that case for now. (You'll see what I mean when you see the example code.)
But think about it. Everything is in one archive and there is also the document (gzipped of course). That's only 2(!) files for people with an unprimed cache. It really can't get any better than that. Well, each content image adds another connection, but that's alright.
Additionally, you always get compression for your JS and CSS files. Of course you can gzip them, but that doesn't work for everybody. About 15% of the visitors of larger websites in the United States have gzip disabled – or more accurately: their Accept-Encoding headers are scrambled by proxies or stupid anti-virus software in order to turn it off. Pretty outrageous, isn't it? But wouldn't it be nice to make their lives a tad better? ;)
While solid archives provide better compression, non-solid archives are better suited for this task. In a solid archive all files are compressed as one big data block, which comes with a few implications. First and foremost you need the complete archive to extract a single file. Additionally, you have to decompress the whole thing even if you only want to extract one of those files.
In a non-solid archive all files are compressed individually, which means you can take a file as soon as it's ready and update the rendering. This also means we can influence the rendering by changing the order of the files in the archive. E.g. favicon first, then CSS, then the logo, then the supporting layout graphics, then icons, and finally the script stuff.
Another benefit of non-solid archives are the per file checksums. If a download error occurs you can a) detect it and b) resume at the right spot. Pretty nifty. Well, download errors shouldn't occur either way, but those poor 56k dudes will be happier.
Interestingly Firefox supports JAR archives, which are basically just Zip files with a different file extension. It's some Netscape 4 (or so) leftover stuff, which was meant to be used for signed pages. (A poor man's https or something? I really don't know.) Fortunately it's still there and it seems to work pretty well.
ZIP/JAR files are non-solid archives which utilize Deflate compression (just like GZip, PNG or SWF) by the way. Each file got its own checksum and a tiny header with the name of the file. So, theoretically there is everything we need for progressive rendering. I haven't tested if Firefox actually does that though, since that isn't really important at this stage.
But enough of that. Check the quick and dirty demo! The demo shows one image, one CSS image, and one favicon from the JAR. (The favicon doesn't work in the online version, since this site's favicon was already loaded – it works fine in the offline version though.)
Note: If you have NoScript installed set noscript.forbidJarDocuments to false. That was a workaround for some vulnerability which was fixed many moons ago with the release of Firefox 220.127.116.11.
It uses the JAR: URI scheme, which is surprisingly simple. It starts off with "jar:" followed by the relative or absolute URL of the JAR, followed by a single forward slash, followed by the path of the resource inside the jar.
<img src="jar:test.jar!/img1.png" alt="img1" width="32" height="32"/>
The same with an absolute URL:
<img src="jar:http://kaioa.com/b/0907/test.jar!/img1.png" alt="img1" width="32" height="32"/>
Of course you can also use directories inside that JAR. E.g. it could look like:
<img src="jar:example.jar!/images/icons/img1.png" alt="img1" width="32" height="32"/>
The CSS image was defined like this:
background:transparent url(jar:test.jar!/img2.png) 0 0 no-repeat;
And the favicon like this:
<link rel="shortcut icon" type="image/x-icon" href="jar:test.jar!/img3.ico"/>
As you can see it's pretty easy stuff. However, there is one catch: The JAR file needs to be served with the right mime type. This isn't required if it's loaded locally though. The mime type needs to be either application/java-archive or application/x-jar. (The file extension doesn't matter by the way. Only the mime type is important.)
Here is how to set it for Apache (.htaccess):
AddType application/java-archive jar
There isn't really anything like graceful degradation in this case. The only thing you can do is load a super simple vanilla CSS and then override it with the one from the archive if possible. That really doesn't look like a good option.
What we really need is support of the JAR: URI scheme in all modern browsers. It doesn't need to be the JAR one, but that one works and it's also a de facto standard used in a bunch of products. Additionally, the format is suitable for this kind of task and all potential submarine patents expired many many years ago.
I don't really know to which spec this kind of thing belongs. HTML5? CSS3? None of those? Either way I want to see it as soon as possible everywhere. It's simple stuff and there are huge benefits. What's not to love about this? :)
Download: jardemo.zip (2kb)