Creating Fancy Checkmark Icons with Pure CSS3

I was recently working on a personal project where I wanted to implement some cool "checkmark" icons like you see at left to spice up my unordered lists. I could've done it quite easily with the list-style-image property, but I was trying to keep my images and http requests to a minimum, so I wanted to figure out another way to do it (plus, the challenge sounded kind of fun).

I had just seen Nicolas Gallagher's awesome pure css social media icons, though, and I thought, "If he can do that, than I could certainly create a simple check icon with just css3". I did figure it out with some experimenting and testing, and today I'm going to show you how to achieve the same effect.

The #1 Rule of CSS3 Coding

Before we start on any project, we have to remember the number one rule of CSS3 coding. Well, actually it's two rules:

  • It must be cross-browser acceptable. I'm not talking identical, but a CSS3 technique is not very useful if it destroys the experience for users with older browsers. Use progressive enhancement.
  • No worthless markup! I know CSS3 is fun, and it's not bad to experiment, but if you have to add a whole bunch of non-semantic junk markup just to save yourself from needing to use an image, you've defeated the point.

These are the two principles that I regard to be the unchangeable law for using CSS3. So anyways, enough of my rant - I just feel with all the buzz about CSS3 that it's very important to use it in a mature and responsible way. Let's move on with the tutorial.

The Concept

Before we can start coding, we have to figure out what approach we're going to take to create these icons. How is it going to be done?

Taking some inspiration from Nicolas Gallagher's work, I think the best way would be to use the :before and :after pseudoclasses to generate the round circle and the checkmark shape. By using the unicode no-break-space (\00a0) as the content attribute, and the setting the display to block and styling it, we can create a huge variety of empty shapes. A checkmark icon lays well inside the boundaries of possiblity!

Also, in order to implement surefire progressive enhancement we're going to use the :nth-of-type selector to make sure that only CSS3 capable browsers try to display the icons (older browsers will  fall back on simple, default bullet-points). The :nth-of-type is a lifesaver and hugely useful for this, because the support for it lines up exactly with support for the border-radius and transform properties that will be necessary to render the icons.

The Code

OK, now that we've got the general concept figured out, we can start coding. We'll start with the html for a basic, unordered list (copied from html-ipsum.com).

[html]

  • Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.
  • Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.
  • Pellentesque fermentum dolor.
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • Aliquam tincidunt mauris eu risus.
  • Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.
  • Vestibulum auctor dapibus neque.

[/html]

We'll start with a very basic CSS style that will be applied to all browsers:
[css]
ul{
margin: 12px;
padding: 0;
}
ul li{
margin: 0 0 12px 0;
font-size: .9em;
line-height: 1em;
}
[/css]

Now, onto the fun part. First, we'll style the list items (for CSS3 browsers only) to have additional padding on the left (to make room for the icons), to be positioned relatively so that we can absolutely position the icons, and to hide the default bullets:

[css]
body:nth-of-type(1) ul li{
list-style-type:none;
padding: 0 0 0 45px;
position:relative;
}
[/css]

Now, using the :before pseudoclass we'll add a small black circle to the left of each list-item - the first half of the icon. We use before so that it will nest behind the checkmark, which will be generated using :after. Here's the code:

[css]
body:nth-of-type(1) ul li:before{
/*fill it with a blank space*/
content:"\00a0";

/*make it a block element*/
display: block;

/*adding an 8px round border to a 0x0 element creates an 8px circle*/
border: solid 9px #000;
border-radius: 9px;
-moz-border-radius: 9px;
-webkit-border-radius: 9px;
height: 0;
width: 0;

/*Now position it on the left of the list item, and center it vertically
(so that it will work with multiple line list-items)*/
position: absolute;
left: 7px;
top: 40%;
margin-top: -8px;
}
[/css]

All that's left is to create the checkmark and put it on top of the circle. We can do that very easily by giving the :after element a white border on the bottom and left, and then rotating it 45 degrees:

[css]
body:nth-of-type(1) ul li:after{
/*Add another block-level blank space*/
content:"\00a0";
display:block;

/*Make it a small rectangle so the border will create an L-shape*/
width: 3px;
height: 6px;

/*Add a white border on the bottom and left, creating that 'L' */
border: solid #fff;
border-width: 0 2px 2px 0;

/*Position it on top of the circle*/
position:absolute;
left: 14px;
top: 40%;
margin-top: -4px;

/*Rotate the L 45 degrees to turn it into a checkmark*/
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
}
[/css]

We're finsished! Here's what the final product looks like:

All that remains now is to integrate it into your design (changing the border colors of the before/after elements if need be)!

Some Thoughts

So that's it. In closing I have a few thoughts about this technique:

On the downside, relying on the :nth-of-type selector is a bit risky, since a browser that recognized that selector but didn't support CSS rounded corners and rotation would end up rendering a distorted version of the icon. Some older versions of Opera have this problem. I think that's a pretty small issue, though, since I'm not aware of any browsers (and certainly not any with more than negligible market share) that fit that description. Also, it could be argued that it's not worth it just to avoid using one small checkmark image. I don't think that's necessarily true, but it is something to consider (for reference, the css code that created the icons is .57kb when compressed).

Overall, though, I think it's a stylish, imageless touch that could fit well into a lot of design situations. It's a creative use of CSS3, it doesn't require extra markup, and it degrades with near perfect gracefulness, so overall I'm proud of the idea and think it's pretty robust.

What are your thoughts? Do you think it's a good idea? I'm looking forward to your feedback!

52 Comments

  1. Jay June 15, 2010
  2. Bill June 15, 2010
  3. Nick Parsons June 15, 2010
  4. Bill June 15, 2010
  5. Fecundvs June 16, 2010
  6. Jacob June 16, 2010
  7. Catherine Azzarello June 16, 2010
  8. ErnieChiara June 16, 2010
  9. Cosmin Negoita June 16, 2010
  10. B. Moore June 16, 2010
  11. Nick Parsons June 16, 2010
  12. Jae Xavier June 16, 2010
  13. Beben June 16, 2010
  14. Webstandard-Blog June 17, 2010
  15. Jaap June 18, 2010
  16. Jaap June 18, 2010
  17. Nick Parsons July 2, 2010
  18. Joomla Web Developers July 20, 2010
  19. Drupal Web Developers July 26, 2010
  20. cathy tibbles August 17, 2010
  21. Justin August 18, 2010
  22. Scott September 4, 2010
  23. harryplusk September 4, 2010
  24. Cory September 4, 2010
  25. Thomas September 5, 2010
  26. Jessicashen September 17, 2010
  27. Robert Tobys September 20, 2010
  28. thomas January 25, 2011
  29. Faraz March 20, 2011
  30. Raju May 2, 2011
  31. Lance May 8, 2011
  32. designarti July 25, 2011
  33. Luke August 15, 2011
  34. Bryan Salva August 19, 2011
  35. Mark March 22, 2012
  36. Derek April 8, 2012
  37. bellakristen April 30, 2012
  38. Gautam Lakum May 12, 2012
  39. Adam Zoa May 31, 2012
  40. reihhana August 14, 2012
  41. Jeff Woodruff September 19, 2012
  42. Jake October 28, 2012
  43. Vipul November 16, 2012
  44. David December 24, 2012
  45. Gute-Mathe-Fragen January 18, 2013
  46. Moazam April 16, 2013
  47. Takagami October 13, 2013
  48. Takagami October 13, 2013
  49. Rasmus Schultz October 15, 2013
  50. Anil December 12, 2013

Leave a Reply