Skip to main content
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Daria Norris
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Daria Norris

Embedding Images As Base64-Encoded Data URIs Using Less CSS

By
Published in Comments (1)

One of the things that I really like about Less CSS is the ability to embed images directly in the generated CSS files using data URIs. A data URI allows one type of content (a CSS file in our case) to embed another type of content (images in our case). Since images are binary, the image content has to be be Base64-encoded so that it can be embedded as text. The syntax is a bit funky; but, it doesn't matter because Less CSS handles it for you. And, the good news is, data URIs are supported back to IE8 (with size-limit caveats).

To demonstrate this feature, I'm going to embed some pictures of monkeys. But, simply using the data-uri() method wouldn't be fun; so, instead, I'm going to loop over a list of image URLs and then dynamically generate a CSS class for each image.

// I loop over the given list and execute the given ruleset for each item.
.list-loop( @list, @ruleset ) {

	// When we define variables inside of a mixin, then get automatially injected into
	// the calling context. This is problematic if we ever want to call this mixin more
	// than once since each subsequent invocation will fail to inject variables (since
	// they cannot overwrite each other). By wrapping the body of this mixin in a block
	// scope, we prevent the automatic variable injection from bubbling up to the calling
	// context. As such, they will be scoped locally to this mixin and to any nested
	// block-scope which can inherit them - IE, our @ruleset.
	& when ( true ) {

		@length: length( @list ) ;

		.loop( 1 ) ;

		.loop( @index ) when ( @index <= @length ) {

			// Extract the item from the list at the given position.
			@it: extract( @list, @index ) ;

			// Invoke the given ruleset - this will inherit the "@it" variable.
			@ruleset() ;

			.loop( @index + 1 ) ;

		}

	}

} ;

// Define the list of images that we want to embed.
@images:
	"../images/monkey-1.jpg"
	"../images/monkey-2.jpg"
;

// For each image in the list, create a derived class with an EMBEDDED image. The
// data-uri() method embeds images as Base64-encoded data URIs.
.list-loop(
	@images,
	{
		.monkey-@{index} {
			background-image: data-uri( @it ) ;
			debug-url: @it ;
			height: 300px ;
			width: 300px
		}
	}
) ;

// Loop AGAIN to demontate that the variables in the list-loop didnt escape scope.
.list-loop(
	@images,
	{
		.monkey-@{index} {
			background-image: data-uri( @it ) ;
			debug-url: @it ;
			height: 300px ;
			width: 300px
		}
	}
) ;

Here, we're leveraging the ability to pass a Less CSS ruleset to a mixin for "execution". You'll notice that I am calling the list-loop() mixin twice; this is to demonstrate the fact that the mixin-based variables aren't leaking up into the calling context, only down into the ruleset invocation. Within each ruleset, the list-loop() mixin is exposing the "@it" variable which contains the image URL. This URL is then being embedded using the data-uri() method.

When we compile the given Less CSS, we get the following CSS output (Note: I have truncated the data URIs for display):

.monkey-1 {
	background-image: url("data:image/jpeg;base64,/9j/....E5oSy6gpzQEGxaZv//Z");
	debug-url: "../images/monkey-1.jpg";
	height: 300px ;
	width: 300px;
}
.monkey-2 {
	background-image: url("data:image/jpeg;base64,/9j/....t2DAHg1NQLkHrNMD//Z");
	debug-url: "../images/monkey-2.jpg";
	height: 300px ;
	width: 300px;
}
.monkey-1 {
	background-image: url("data:image/jpeg;base64,/9j/....E5oSy6gpzQEGxaZv//Z");
	debug-url: "../images/monkey-1.jpg";
	height: 300px ;
	width: 300px;
}
.monkey-2 {
	background-image: url("data:image/jpeg;base64,/9j/....t2DAHg1NQLkHrNMD//Z");
	debug-url: "../images/monkey-2.jpg";
	height: 300px ;
	width: 300px;
}

Pretty cool stuff!

The nice thing about this approach is that it reduces the number of HTTP requests that need to be made to gather the content (since the content is partially embedded in the CSS). Of course, there are trade-offs to be considered around things like upfront load time, deferred loading, and retina images. But, that's beyond the scope of this post.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Thanks for the post =), using this for a long time now (without less, just php minifing/concatinating/datauriing etc. script)...just wanted to note, that a drawback is also the inability of the browser to cache the image.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel