Icon links with data-uris (no CSS sprites !)

One step in getting to a snappy website experience is reducing HTTP requests. One technique contributing to this reduction is using CSS sprites for icon links. Today, however, I’ll show you how you can achieve the same objective using data-uris. The main advantage is that you’ll be able to work with your icons as single images, instead of having to organize them into a single image and keeping track of image offsets.

Introducing data-uri

Data-uri is essentially a means to include data within files. Where this really comes in handy is when designing icon links with CSS: you can reference the image you wan to use as an icon, and embed it in the CSS file. With this technique, the number of HTTP requests is reduced: the browser only has to require the stylesheet, since the icon image will already be included inline.

What we’re after

In one word (or image, I should say…) : As you can see, they’re simply image links using icons to convey meaning. Naturally, they also have “alt” and “title” attributes set, for 2 reasons: so screen readers can make sense of the links, and so that users can hover on the icon to see the action it corresponds to.

Visually, there is no way to differentiate these image links from ones derives from CSS sprites. The main difference will be in HTTP queries: the images above required no further requests beyond the CSS stylesheet.


I implemented this in a Rails 3.1 project, so you’ll be seeing some Sass. First, I created a mixin for icon styling: I knew I wanted to start with at least 2 icon sizes (16 and 24 pixels), so mixins were the way to go:

@mixin icon($size, $offset) {
width: $size+px;
height: $size+px;
margin-bottom: -$offset+px;
margin-left: -$offset+px;
padding-right: $offset+px;
display: inline-block;

Then I still had to actually generate the Sass code. I went with ERB, since it’s easier to integrate inline content than with HAML:

<% { small: [16, 3], medium: [24, 6] }.each do |size, values| %>
.icon.<%= size %> {
@include icon(<%= values[0] %>, <%= values[1] %>);
<% ['plus', 'right-arrow', 'edit', 'delete'].each do |icon_name| %>
&.<%= icon_name %> {
background: url(<%= asset_data_uri “#{icon_name}-#{size}.png” %>) no-repeat;
<% end %>
<% end %>

As you can tell, we’re simply creating rules for 2 icon sizes, “small” and “medium”. Then for each group, we create a separate CSS rule specifying the icon’s background image. And that is where the data-uri magic happens: you can specify `asset_data_uri “my_icon_name”` within ERB tags, and your image will be included inline within the CSS file. Pretty neat, no ?

Where the data-uri technique is really nice than CSS sprites is that you can see it requires little work on your part: icon files stay separate (i.e. no need to create a sprite image), and will be included inline as data-uris during asset compilation.

Please note that the two code snippets go in the same file. I personally put them in `app/assets/stylesheets/icons.css.scss.erb`. Although the file’s name isn’t important, it’s extension is: ending in `.css.scss.erb` means it will be aprsed by ERB, before being passed on to Sass, then CSS. Any other order won’t work properly, but if you respect the conventions, the asset pipeline will work its magic for you.

Displaying the icon link

Now that we have all this in place, let’s display an icon link to an `edit` action:

icon = "<span class='small icon edit'></span>".html_safe

link_to icon, edit_user_path(@user), title: 'Edit'

Browser capabilities

Different browsers have different capabilities with respect to data-uris, by which I mean IE < 8 can’t do it… What can you do about it? There are some MTHML techniques to achieve the same result that you could use. Personally, I chose not to do anything about it: the beauty of progressive enhancement is that browser that are more advanced will provide a better user experience, while older, less capable browser will still provide the required functionality. In other words, old IE browsers will continues to see a plain vanilla ‘Edit’ link. Good enough, and it will save many headaches. After all, at some point we have to decide that the web is moving forward, and older browsers won’t get the same level of UI functionality as modern browsers: users can update their browsers to benefit from an enhanced experience, or remain with outdated browsers and still be provided a usable interface. In essence, progressive enhancement will keep you sane.

This entry was posted in Rails. Bookmark the permalink.

Comments are closed.