How to make complex icons with only CSS


Coded CSS icons have some significant advantages over icon fonts or static images. CSS icons are a part of the DOM and unless you’re using a CSS icon library like Icono, they don’t require another HTTP request. Just like font icons, CSS icons can be colored by CSS and You can also animate the icons in an easy, smooth way without much effort.

You don’t always need a full icon set like FontAwesome, sometimes just a few icons are needed. For some of our applications at Truth Labs, we’ve designed a minimalist set of a few pure CSS icons. In this post, you’ll learn what our process was and how you can make your own dynamic and unique CSS icons.

On the left, the icon we’ll be making. on the right, how it is structured in HTML and CSS.

Guiding Principles

There certainly isn’t a right and wrong way to make CSS icons. Like many decisions, you need to choose your tradeoffs. If you want a complex icon, you may need to use more than one element. If the icon is clickable, you need to stay mindful of the element’s size to ensure it has a good-sized, predictable hitbox (touch target or click target).

I like to use only one element for each icon. There’s less to mess up when you’re working in the HTML, and there’s almost never a need to use more than one element. The two pseudo-elements (:before and :after) can help you create almost any icon you can think of!

I also like to size my icons using percentages and “em”s so that my icons will scale with font size. This means that I can reuse icons at different sizes around the page without having to do pesky arithmetic or thinking.

Let’s make something

This post is centered around a tough icon (or “eye-con”…), walk through the thought process while creating it. It breaks down the steps necessary to make excellent CSS icons.

Here is the result that I’m looking for:

Before diving into code, the first step is to break the icon down into its underlying shapes. Upon close inspection, this icon is made up of two elements: the main grey eye shape and the white outlined circle in the center.

I’m going to completely rely on the pseudo-elements for the shapes and use the parent element as the clickable element.

Now that I know what shapes I need, I need to figure out how to get them from the two pseudo-elements (:before and :after) I have to work with.

The white circle is fairly simple: a center-positioned element with a white border and a border-radius of 100%. There’s nothing tricky going on there.

A harder puzzle to solve is the grey eye shape. Remember, the first task is to break the shape down into its underlying shapes. One thing to notice is that the corners of the eye are 90 degree angles. This is a good clue that we might try a rotated square (diamond) shape.

Once you see the eye as a square rotated 45 degrees, it’s much easier to see what to do next: round the top and bottom corners. Here it is in action:

Add some animation

Using CSS transitions on hover or between states can make stuff look absolutely awesome. Once you get started, all kinds of wonderful ideas will start revealing themselves. Transitions for menu states, search bars, and basically any interaction provide opportunities for slick transitions and creative use of motion.

Here is an example of the eye animating with CSS to create the animated diagram above.

Using the :hover selector lets you make hovering result in a looping CSS animation, like a car bouncing up and down as it drives along the road, or a steering wheel gently turning back and forth. Try going over the top, then dial back to a good amount of motion.

Play around with it and see what you can come up with! Here’s another Codepen example using the Truth Labs logo and the animation featured on our site:

Starter Set

Here is a set of icons that I’ve made out of one <div> element. Feel free to use them and look at them for ideas for your own icons. Be sure to look through the Codepen to see how it is done.

Technical Tips

The box model has a huge impact on where elements end up. Make sure you understand what property does what, and in what browser. If you aren’t using something like Normalize, you should at least set the box-sizing to border-box for all. Here’s a box-model diagram explaining border-box vs content-box:

The box at the top shows the default box model. The box at the bottom shows what was once the “quirks mode” interpretation of the box model. (Credit for diagram: Paul Irish)

Pseudo-elements are tricky. They aren’t selected with the * selector, so you need to target them specifically to get box-sizing applied correctly. They also require at least display and content attributes to be set in CSS in order to be rendered.

Position relative and absolute are important to learn as well. Along with the box model, they will help you feel more confident about the placement of your elements and the cross-browser safety of your icons.

And of course, when you find something cool that someone else has done, right click and inspect element.