Hashrocket.com / blog

Large key

Better Icon Fonts with Sass

posted on and written by Cameron Daigle in

Image 100x100 cameron daigle

When I think about all of the different ways I've made icons over the past dozen or so years, I start to feel a few different emotions. Old, primarily. But nostalgic? Hardly. We've come a long way, folks.

I'm Going to Talk About Obsolete Stuff For a While

In the (well, my) early days, if I wanted an icon that changed color on hover, I'd make two versions of an icon (icon_check.jpg, icon_check_hover.jpg), and then preload them with Javascript, to prevent my early-2000s-era Internet from causing the button to flicker on hover as the second image was loaded in. (Remember how MM_preloadimages() was on the body tag of half the Internet back then? Yeah, that.)

Swing on down to the heady days of 2004: A List Apart publishes an article on CSS sprites. That article changes everything. I start dropping every version of every icon into one gigantic image, each positioned via boatloads of background-position coordinates scattered throughout my CSS. Hooray for progress!

Fast forward a handful (okay, a lot) of years, and RETINA SCREENS appear. Congratulations, your icons now look like blurry crap! I sigh heavily, admit that spritesheets always felt kind of hacky anyway, and accept a future in which I must redraw every icon at @2x and even @3x sizes. So now instead of maintaining one spritesheet of 20 icons, I now maintain 40 separate images. Hooray, for, uh, progress?

Okay, This Is The Useful Part

But it's 2015, and thankfully, we've since moved on from those dark times. Icons are generally vector data now, allowing for unlimited rescaling and recoloring. Font Awesome and other prebuilt icon fonts are there for the taking, reducing a decade's worth of technical knowledge to semi-nostalgic-blog-post status.

I generally prefer to start from scratch on new projects rather than tossing in an entire icon library, and Fontastic has been our tool of choice for creating custom icon fonts. I'm not crazy about the CSS it spits out, though – the globs of repeating code it generates (a separate CSS class for each icon, etc. etc. is just begging for some Sass streamlining.

So let's go for it! First of all, characters map to icons, which is the perfect job for a Sass map:

$icons: (
  "plus": "a",
  "minus": "b",
  "email": "c",
  "arrow-right": "d"
);

Now that we've set up a map, each new icon is just a new line in the map. Everything else we're going to do can remain the same, which makes me happy.

Next up, here's a set of icon mixins that append the icon as either a :before or :after element, with the actual inclusion code abstracted into its own mixin in case we need to inject it in different ways:

// if we just need the content attribute

@mixin icon-content($icon) {
  content: map-get($icons, $icon);
}

// if we need the content attribute with our default styling

@mixin icon($icon) {
  display: inline-block;
  vertical-align: middle;
  text-transform: none;
  font-family: "my-sweet-icons";
  @include icon-content($icon);
  font-weight: normal;
}

// if we want it in a :before or :after, with some default spacing

@mixin icon-after($icon) {
  &:after {
    @include icon($icon);
    margin-left: 1rem;
  }
}

@mixin icon-before($icon) {
  &:before {
    @include icon($icon);
    margin-right: 1rem;
  }
}

And finally, let's generate a set of classes, for situations where we don't want to inject the icon directly via a mixin.

// generates .icon-plus, .icon-minus, and so on

@each $icon, $char in $icons {
  .icon-#{$icon} {
    @include icon-before($icon);
  }
}

There we are. Regardless of your chosen font generation method, this method allows you to update your icon font with a simple change to your $icons map, and allows you to drop in your icon at your leisure via mixin or class. Check back in 2020 when all of this becomes obsolete! We'll laugh and laugh, you and I.

Posted in Design