Techniques to add icons with CSS and Sass

We all want a good HTML structure. It gives meaning to the content and makes content accessible to crawlers and screen readers. Most users like a visual experience though, a plain HTML page is too flat for the majority of users (although, good typography helps a lot). Icons and vector art are the best ways to make a website look more lively, without sacrificing the readability of a page.

Ideally, we should have clean HTML with icons added with CSS. But adding icons in a correct manner is sometimes really hard. Especially when using content management systems (like Drupal or WordPress) with limited control over the markup. For example, there is often complex logic behind the template files for menus and fiddling with these takes more time than a CSS-oriented approach.

Also, today we live in the post pixel-perfect age. Responsive webdesign uses liquid layouts, where the use of percentages versus pixels is often the best option. Also, lots of users visit the web with HiDPI devices. Apple, for example, is shifting rapidly to Retina for almost all devices, and Android and Microsoft devices are following rapidly. So, we need a solution that respects these screens with double pixel density and future screens with more then double pixel density.

So, we need a system that respects the semantics of the web as much as possible, that is flexible by using CSS and that is scalable for the HiDPI screens of the future.

I will show some techniques I’m currently using and some techniques I have abandoned.

PNG icon sprite (old way)

This is the way I used to add icons. The biggest advantage is the possibility to have a nice hover effect on the image.

Add an empty div:

<span class="logo-facebook">
  <a href="" title="Find us on Facebook"></a>

Add this CSS:

.logo-facebook a {
  display: block;
  width: 120px;
  height: 31px;
  background-image: url('');
  background-position: 0 0;
  background-repeat: no-repeat;

.logo-facebook a:hover {
  background-position: 0 -31px;


A sprite is a collection of images. It limits the amount of http requests. In this particular case, we add a hover effect. Without a sprite, the image would be loaded during the hover action, which is not recommended at all. So, it’s good performance wise but in the case of a hover, it’s also necessary.

The above image is not suited for HiDPI devices. A rather dirty trick is to use double the image size, so: width 120px and height 62px. Though that trick won’t work on devices with even more pixels then x2.

Icon fonts

A common approach on the web is the use of icon fonts. I feel this approach can be a bit bloated when you need five icons on a website. In most cases, the whole font collection will be downloaded. Also, lots of unnecessary classes are added, which degrades the semantics of the page.

But it’s one of the easiest techniques to use and also the most common technique in a world where Bootstrap is everywhere.

Icon-fonts are fonts, so they can have color, size, padding, line-height,…

The easiest way is to add an empty icon container with reference to the glyph (of course, you have to add the font to the website):

<i class="fa fa-camera-retro"></i> fa-camera-retro

Very easy!

When it’s not possible to add the icon tag and the class, you can use a pseudo-element to add the icon.

<div class="search"></div>

With this CSS:

.search {
  height: 40px;
  border: 1px solid #888;
  width: 250px;
  margin: 30px;
.search:before {
  display: inline-block;
  line-height: 1.2;
  margin: 10px;
  font-family: "FontAwesome"; 
  content: "\f002";
  color: #888;

Cheatsheet to find the unicode characters.

These icons are, just like fonts, vectors. They will be sharp on every device.

SVG image replacement

I prefer using svg’s to replace text with an icon or to place an icon next to text. An SVG is basically code, so you can edit for example the color in the code. SVG’s scale endlessly. And support is ok, the only drawback is no support in IE8. When you need IE8 support, use modernizr with a .no-svg fallback (and add an png file).

Add icon to text

Leaving the text as it is, is by far the easiest:

<div class="text">
Some interesting text

And some CSS:

.text {
  padding: 50px 150px;
  background-position: 5px 50%;
  background-size: 100px 100px;
  background-repeat: no-repeat;
  background-image: url("");


This can also be accomplished with a :before pseudo element but I try to avoid these as much as possible.

Replace text

Another approach is replacing the text with an image. For example, for a site name. I like to use the site name and replace it with a logo. The text goes off-canvas with front-end code.

  <a href="/">Site name</a>

And I use these lines of CSS, referred to as the Scott Kellum method (

.ir a {
    width: 100px;
    height: 100px;
    background-image: url('');
    background-size: 100px 100px;
    text-indent: 100%;
    white-space: nowrap;
    overflow: hidden;

The only disadvantage is that, when searching the page with CTRL+ F or CMD + F, the text will be visible again. But for search engines and screen readers, this method is excellent.


SVG image replacement with inline encoding

But when we want a nice hover effect, we can’t use this method. We need a method to load the image directly. For that use case I prefer a Sass/Compass helper function, inline-image, starting from the html above:

<h1 class="ir">
  <a href="/">Site name</a>

And adding theses lines of CSS:

.ir a {
    margin-top: 10px;
    margin-left: 15px;
    text-indent: 100%;
    white-space: nowrap;
    width: 16px;
    height: 16px;
    overflow: hidden;
    background-size: 16px 16px;
    background-repeat: no-repeat;
    background-position: center;
    background-image: inline-image("../images/zoeken.svg");
    &:hover {
     background-image: inline-image("../images/zoeken-hover.svg");

Sorry, no CodePen link here because there is no support on CodePen for inline-images.

The inline images will be written directly to the CSS file as a base-64 encoded image. I use this mainly for general icons, found everywhere on the website, and for icons with a small file size. It's a bunch of markup but the markup will be in the .css files, not in the .scss files.

When using a fallback for IE8, using modernizr, I use a .no-svg class with a png fallback, but omitting the hover effect (progressive enhancement wise this shouldn’t be a problem.

When using Sass with Compass, you could also use a helper function, squish-text. Just like the above method, the text will be available for crawlers and screen readers but we can present a nice icon to normal humans.

.ir a {
@include squish-text;

This compiles as follows (using the HTML5 Boilerplate method):

.ir a {
  font: 0/0 serif;
  text-shadow: none;
  color: transparent;

More info about Compass text replacement methods.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.