Using Data URIs

After reading Nicolas Zakas article, Data URIs make CSS sprites obsolete, I was inspired to see how easy it would be to put into practice. His CSSEmbed JAR uses Base64 encoding for data URIs and MHTML to be backwards compatible with IE 6 and 7. So the logical step was to create a build script that runs CSSEmbed against all my CSS files.

Getting ready

You will need to install a copy of ant and ant-contrib to use these build scripts. To include ant-contrib into your ant build script, use:

<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="pathToJar/ant-contrib-1.0b3.jar"/>

Additionally, you will need the CSSEmbed JAR.

How to do it…

First setup some properties to be used by the ant target:

<property name="datauri.jar" value="pathToCSSEmbed/cssembed-0.3.3.jar"/>
<property name="datauri.charset" value="UTF-8"/>
<property name="datauri.inputdir" value="inputDirectory"/>
<property name="datauri.mhtmlroot" value="URLofCSSorDomain"/>
<property name="datauri.outputdir" value="outputDirectory"/>
<property name="datauri.root" value="URLofCSSorDomain"/>

Next create your ant target:

<target name="create.css.datauri">
	<for param="file">
		<fileset dir="${datauri.inputdir}" id="css.datauri.files">
			<include name="*.css"/>
			<propertyregex property="datauri.filename" input="@{file}" regexp="(\w+\.css)" select="\1" override="true"/>
			<echo message="converting ${datauri.filename} to datauri"/>
			<java jar="${datauri.jar}" fork="true" failonerror="true">
				<arg value="-o" />
				<arg value="${datauri.outputdir}/${datauri.filename}" />
				<arg value="--charset" />
				<arg value="${datauri.charset}" />
				<arg value="--root" />
				<arg value="${datauri.root}" />
				<arg value="@{file}" />
			<java jar="${datauri.jar}" fork="true" failonerror="true">
				<arg value="-o" />
				<arg value="${datauri.outputdir}/ie-${datauri.filename}" />
				<arg value="--mhtml" />
				<arg value="--mhtmlroot" />
				<arg value="${datauri.mhtmlroot}" />
				<arg value="--charset" />
				<arg value="${datauri.charset}" />
				<arg value="@{file}" />

How it works…

The trickiest part when using CSSEmbed is the root and mhtmlroot properties. These URLs are not the location of the files on your computer, but on the web, and varies depending on how you reference the images in your CSS. If the images are relative to your CSS, then the values should be set to the URL of your CSS directory. If the images are relative to the root of your domain, then these values should be your URL of your root domain. CSSEmbed will make JAVA net calls when generating the URIs.

The ant-contrib project was necessary, because basic ant does not have a way to iterate on a fileset. I tried to simulate this using macros, but couldnt get it working correctly, so I settled on using ant-contrib.

In the build target, the for iterates on the fileset of CSS files in your CSS directory, storing the value as the param @{file}. As written, it will only search that immediate directory, and not subdirectories. To search subdirectories as well, change *.css to **/*.css.

The propertyregex task finds the filename of the file, by removing the absolute path from @{file}. The CSSEmbed JAR is executed twice: once to create the non-IE CSS and a second time to create the IE 6 & 7 CSS. Your code will need to branch serving a different file depending on the browser. You can do this client-side using conditional comments:

<link type="text/css" href="/build/css/dataUriTest.css" rel="stylesheet"/>
<!--[if lt IE 8]>
	<link type="text/css" href="pathToCSS/ie-dataUriTest.css" rel="stylesheet"/>

However, this is less efficient than branching server-side, because the IE version contains all the same CSS as the non-IE version (only the URIs vary).

Whats next?

I ran out of time while writing this article, but I would have liked to write a build step that strips out all CSS rules from the IE version, except those that contain data URIs. This would reduce the overhead of client-side branching, using conditional comments. However, the most efficient way of branching would still be server-side, as the data URIs would be duplicated in any client-side branching system.