Wednesday, November 24, 2010

CSS Data URIs – Use Them In All Browsers Now!

CSS Data URIs – Use Them In All Browsers Now!


Data URIs are one of the best techniques in CSS, allowing developers to avoid referencing external images and instead embed them directly into a stylesheet. The main advantage of this approach is to save HTTP requests.
HTTP requests are a huge performance bottleneck, and the reason techniques such as CSS image sprites have been popular for some time. Basically, if you can avoid requesting an extra file, not only does it save your server the work of looking up the file, but it also saves your user the download time. In fact, HTTP request management is so important, that it is the top issue in the Yahoo Performance Rules.
Data URIs are an excellent way to reduce HTTP requests and speed up your pages, so let’s walk through how to use them in all major browsers.

When To Use Data URIs

When used instead of an image sprite, data URIs save a single HTTP request, and every little bit counts. However they are even more useful for images that are difficult to include in sprite sheets, for instance custom list bullets that need a varying amount of whitespace.
Although data URIs are an excellent way to reduce HTTP requests, it doesn’t make sense to use them in every situation. Since they embed the raw file data directly in the stylesheet, data URIs can lead to stylesheet bloat if they are used heavy-handedly.
Data URIs are great for any imagery that is repeated on all the pages of your site. However, for page-specific images it is usually better to reference an external image in the stylesheet. Since the file data is embedded directly in the stylesheet, data URIs will be downloaded by all your site’s visitors, regardless of whether they hit the page with that particular image. That said, you can feel free to embed page-specific data URIs on the individual page, just take care not to include them in a site-wide stylesheet.

How To Use Data URIs

Fortunately embedding data URIs is relatively simple. First you’ll need to generate a text string of the raw image data. For this I like to use the Base64 Online Generator.
Once you have the image data, simply place it directly in your stylesheet as an inline background image:
blah {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANS ...
UhEUgAAABgAAAAYCAMAAADXqc3KAAADU5ErkJggg==");
}
Here we’ve used image/png to specify the content type, but make sure to change this to image/jpg or image/gif depending on the MIME type of the image you’re embedding. Additionally make sure to keep the data URI all on one line without line-breaks.

Supporting Data URIs in IE

Kuato Lives IEFirefox, Chrome, Safari and Opera all support data URIs. However, as you may have guessed data URIs are not fully supported in IE, so special accommodations need to be made for everyone’s favorite browser.

Data URIs in IE8

IE8 mostly supports data URIs with a few minor caveats. The main problem is that IE8 data URIs cannot exceed 32kb, however this is not a huge issue, since embedded images rarely exceed this limit.
Additionally, data URIs can only be used for a handful of HTML elements in IE8: <object>, <img>, <input type="image"> & <link>. But this only concerns markup, and when it comes to CSS, IE8 allows data URIs on any element. Finally, IE8 data URIs can only be used in CSS declarations that accept a url() parameter, however since data URIs are rarely used differently, this is basically a non-issue.

Data URIs in IE6 and IE7

While IE6 and IE7 don’t technically support data URIs, we can achieve something similar using MHTML and a technique pioneered by Stoyan Stefanov.
First include the images as MIME data within a stylesheet:
/*
Content-Type: multipart/related; boundary="MYSEPARATOR"

--MYSEPARATOR
Content-Location: image1
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAD....U5ErkJggg==
--MYSEPARATOR
Content-Location: image2
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAA....U5ErkJggg==
--MYSEPARATOR--
*/
Be careful with the separators here, or you will have issues with Vista / Windows 7. The boundary declaration can be used to define any separator string you want, however be sure to start each new data block with --MYSEPARATOR and end the MIME data with --MYSEPARATOR--.
Next, reference the MHTML images in your stylesheet:
/*
The MIME data from above goes here
*/

.image1 {
  background-image: url("data:image/png;base64,[raw data here]");
  *background-image: url(mhtml:http://mysite.com/styles.css!image1);
}

.image2 {
  background-image: url("data:image/png;base64,[raw data here]");
  *background-image: url(mhtml:http://mysite.com/styles.css!image2);
}
Here we’re first including the standard data URI for most browsers, then using the star hack to define the MHTML image data for IE6 and 7. Note the url in the mhtml declaration; it uses the stylesheet’s url followed by the Content-Location defined in the MIME data section.
However this technique has one clear drawback, which is that we are now including the image data twice on one page. Considering the large size of raw image data, it doesn’t make sense to include it twice unless you’re embedding very small images.
Fortunately this issue can be avoided in a number of ways. One approach might be to use server side browser sniffing to only enable the MIME data for the affected browsers.
Alternately, you can use browser conditionals to include a separate stylesheet for IE7 and lower. This has several advantages, including being able to attach other browser-specific styling without relying on CSS selector hacks.
However, don’t include an IE7 stylesheet on top of your main stylesheet, or you’ll defeat the purpose of reducing HTTP requests. Instead include separate stylesheets for both:
<!--[if !(IE)|(gt IE 7)]><!-->
<link rel="stylesheet" type="text/css" href="main-styles.css" />
<!--<![endif]-->
<!--[if lte IE 7]>
<link rel="stylesheet" type="text/css" href="ie7-styles.css" />
<![endif]-->
Here we’ve included the stylesheet main-styles.css for IE8 and non-IE browsers, as well as ie7-styles.css for IE7 and below. Although somewhat more difficult to maintain, this approach ensures the lowest number of HTTP requests (and these stylesheets can be built dynamically as part of a build process).

Using Data URIs For Fonts

Data URIs aren’t only useful for images, they’re also a great way to reduce HTTP requests for fonts embedded with @font-face.
Embedding fonts with data URIs is the same as embedding images, except with a different MIME type:
@font-face {
    font-family: "My Font";
    src: url("data:font/opentype;base64,[base-encoded font here]");
}
To generate the raw font data, you can use the base64 generator we discussed earlier, or better yet use Font Squirrel’s @font-face generator.
Simply use the “expert” mode and enable the “Base64 encode” option:
Font Squirrel's @font-face generator
Unfortunately there are a few notable drawbacks to using @font-face data URIs. First, Font Squirrel states that SVG and EOT file types do no support data URIs. However as Aaron Peters proves, EOT can be supported (although SVG is still not an option).
Additionally, unlike images which use the same data URI across all browsers, @font-face uses several different browser-specific implementations. Considering the relatively large size of font files, it would be a mistake to embed all these font files in a single stylesheet. So similar to the MHTML example above, use server side browser sniffing, or a similar method to serve the data to only the appropriate browsers.
Thanks to Stoyan Stefanov for all his wonderful posts on Data URIs.

http://jonraasch.com/blog/css-data-uris-in-all-browsers

No comments: