Styling Elements With Glyphs, Sprites and Pseudo-Elements

Advertisement

In 2002, Mark Newhouse published the article "Taming Lists1", a very interesting piece in which he explained how to create custom list markers using pseudo-elements. Almost a decade later, Nicolas Gallagher came up with the technique pseudo background-crop2 which uses pseudo-elements with a sprite.

Today, on the shoulders of giants, we’ll try to push the envelope. We’ll discuss how you can style elements with no extra markup and using a bidi-friendly high-contrast proof CSS sprite3 technique. The technique will work in Internet Explorer 6/7 as well.

Displaying icons in links and as custom list markers

Starting with special characters

There is a plethora of glyphs4 out there that we could use instead of images to create custom markers. This should improve:

  • performance (there is no HTTP request)
  • usability (these characters will grow or shrink according to user’s settings)
  • maintenance (no sprite to create, no asset to deal with)
  • accessibility (see further below).

Example:

The markers (♠, ♣, ♥, ♦) in the list above are created via the following rules:

HTML:

<ul class="glyphs"> 
	<li class="one">performance</li>
	<li class="two">usability</li> 
	<li class="three red">maintenance </li> 
	<li class="four red">accessibility</li> 
</ul>

CSS:

.glyphs {
  list-style-type: none;
}

.glyphs li:before,
.glyphs b {
  display: inline-block;
  width: 1.5em;
  font-size: 1.5em;
  text-align: center;
}

.one {
        background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = ''+this.innerHTML);
}
.one:before {
        content: "2660"; /* ♠ */
}
.two {
        background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = ''+this.innerHTML);
}
.two:before {
        content: "2663"; /* ♣ */
}
.three {
        background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = ''+this.innerHTML);
}
.three:before {
        content: "2665"; /* ♥ */
}
.four {
        background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = ''+this.innerHTML);
}
.four:before {
        content: "2666"; /* ♦ */
} 

.red b,
.red:before {
  color: red;
}

How does this work?

  • The value of the content property must be an escaped reference to the hexadecimal Unicode character5 value (for IE, we use HTML entities6).
  • Internet Explorer 6/7 do not support ::before nor :before, so the characters are plugged via CSS expressions.
  • IE8 does not support ::before, but does support the single colon notation
  • Please notice that putting aside browser support, “there’s no difference7 between :before and ::before, or between :after and ::after. The single colon syntax (e.g. :before or :first-child) is the syntax used for both pseudo-classes and pseudo-selectors in all versions of CSS prior to CSS3. With the introduction of CSS3, in order to make a differentiation between pseudo-classes and pseudo-elements, in CSS3 all pseudo-elements must use the double-colon syntax, and all pseudo-classes must use the single-colon syntax.”
  • In IE, characters are wrapped in <b> elements, so we have a means to target and style them (you may rather want to rely on a class name for that).

Note that the CSS expressions we use here are not as bad as the ones generally used to mimic min-width8 and the like. These are only evaluated once, which should result in a small performance hit.

Displaying Images Via Pseudo-Elements

The main advantage of using a pseudo-element for the sole purpose of displaying an image is that it allows designers to crop a sprite. Actually, this is nothing new, and many websites are already using extra (aka "junk") markup to achieve this. For example, Yahoo! Search uses empty <s> and Facebook uses empty <i> tags for this purpose. Going this route allows for the creation of compact CSS sprites, without empty space between the images within the sprite.

The two examples below do not use extra markup and they both share the same sprite:

sprite

The two images below — which are the second icon in the sprite — are generated using each technique, respectively.

Nicolas Gallagher’s method

Styling the pseudo-element with a background image:
#first:before {
    content: "";
    float: left;
    width: 15px;
    height: 15px;
    margin: 4px 5px 0 0;
    background: url(sprite.png) -15px 0;
}

The new url() / clip method

Using the content property to insert the sprite which is then cropped with clip:
#second {
  position: relative;
  padding-left: 20px;
  background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<img alt="" src="sprite.png">'+this.innerHTML);
}

#second:before,
#second img {
  content: url(sprite.png);
  position: absolute;
  top: 3px;
  clip: rect(0 30px 15px 15px);
  left: -15px; /* to offset the clip value */
  _left: -35px; /* some massaging for IE 6 */
}

In case you wonder why I use position: absolute in the above rule, it is because the clip property only applies to absolutely positioned elements.

The New Technique: How Does It Work?

  • Instead of styling the pseudo-element with a background, we use it to insert an image (via content).
  • Using the clip property, we crop this image to only display the part we want to show. It means that there is no need to add empty space in the image to avoid other parts to show as well (usually used as background image of larger elements).
  • We offset clip values by using the left and/or top properties.

With a non-cropping technique, images in sprites would have to start from the right hand side or left hand side to accommodate RTL/LTR contexts (background-position: [left]|[right] [vertical value]). Another limitation is creating sprites with images showing next to each other (because other images could be displayed as well). But when cropping sprites, these issues are not in play, so all images can be tucked together.

For an example, see figure below:

Two sprites for different contexts versus one sprite for both LTR and RTL interfaces

Advantages of this method over existing techniques

Styled to print
Unlike background images, these images are printed with the document (they are sent to the printer).

Styled to be accessible
Unlike background images, these images will not disappear in MS Windows’ high contrast mode9 or with high-contrast styles sheets10.

Styled to work in IE lt 8
This method works in Internet Explorer 6 and 7 as well.

Note that data URI scheme11 could be used to avoid the HTTP request. IE6/7 do not support data URI scheme, but we can use MHTML for IE6/712 to make IE7 and older browsers understand it as well.

Styling links with pseudo-elements

Nicolas Gallager shows plenty of cool stuff13 one can do with pseudo-elements. The only thing I’d add here is the use of ::after to style links à la "read more" and the like, for example:

Read more14

CSS:

.more:after {
        white-space:nowrap;
        content: " 0BB"; /* » */
}
.more {
        white-space:nowrap;
        background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = this.innerHTML+' »');
}

A word about accessibility

You should assume that generated content is read by screen-readers15, and since there is no mechanism to offer alternative text for images plugged via the content property, we should make sure those images are purely decorative because screen-reader users would not have access to that information.

Further reading

You might want to take a look at the following related resources:

Credits: Icons by FatCow Web Hosting21 [CC-BY-3.0-us22], via Wikimedia Commons23

dt{ font-weight: bold; }
.glyphs {list-style-type:none;}
.glyphs li:before,
.glyphs b {display:inline-block;width:1.5em;font-size:1.5em;text-align:center;}
/* advantage of grouping these: the rule will show in Firebug :) */
.one,
.one:before {
content: “2660″; /* ? */
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
}
.two,
.two:before {
content: “2663″; /* ? */
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
}
.three,
.three:before {
content: “2665″; /* ? */
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
}
.four,
.four:before {
content: “2666″; /* ? */
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
}
.red b,
.red:before {color:red;}
.one:hover:before,
.two:hover:before,
.three:hover:before,
.four:hover:before {color:teal;}
#first:before {
content:””;
float:left;
width:15px;
height:15px;
margin:4px 5px 0 0;
background:url(http://coding.smashingmagazine.com/wp-content/uploads/2011/03/sprite.png) -15px 0;
}
#second {
position:relative;
padding-left:20px;
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
}
#second:before,
#second img {
content:url(http://coding.smashingmagazine.com/wp-content/uploads/2011/03/sprite.png);
position:absolute;
clip:rect(0 30px 15px 15px);
top:3px;
left:-15px; /* to offset the clip value */
_left:-35px; /* some massaging for IE 6 */
}
.more,
.more:after {
white-space:nowrap;
content: ” 0BB”; /* » */
background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = this.innerHTML+’ »’);
}

Footnotes

  1. 1 http://www.alistapart.com/articles/taminglists/
  2. 2 http://nicolasgallagher.com/css-background-image-hacks/demo/crop.html
  3. 3 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
  4. 4 http://inamidst.com/stuff/unidata/
  5. 5 http://www.fileformat.info/info/unicode/char/2666/index.htm
  6. 6 http://www.w3.org/TR/html40/sgml/entities.html
  7. 7 http://www.impressivewebs.com/before-after-css3/
  8. 8 http://robertnyman.com/2007/11/13/stop-using-poor-performance-css-expressions-use-javascript-instead/
  9. 9 http://www.artzstudio.com/2010/04/img-sprites-high-contrast/
  10. 10 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
  11. 11 http://en.wikipedia.org/wiki/Data_URI_scheme
  12. 12 http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/
  13. 13 http://nicolasgallagher.com/css-background-image-hacks/
  14. 14 #
  15. 15 http://lists.w3.org/Archives/Public/www-style/2010Nov/0437.html
  16. 16 http://www.w3.org/TR/2002/WD-css3-lists-20021107/#markers
  17. 17 http://www.w3.org/TR/CSS2/generate.html
  18. 18 http://www.w3.org/TR/CSS21/visufx.html#clipping
  19. 19 http://juicystudio.com/article/colour-contrast-css-sprite-maps.php
  20. 20 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
  21. 21 http://www.fatcow.com/free-icons/
  22. 22 www.creativecommons.org/licenses/by/3.0/us/deed.en
  23. 23 'http://commons.wikimedia.org/wiki/File:Farm-Fresh_bullet_feed.png'

↑ Back to topShare on Twitter

Thierry is passionate about Web Design and CSS. He loves the challenge of solving problems and dreaming up original ideas that get him out of bed in the morning and keep him working late at night. He is a front-end engineer at Yahoo! and owns TJK Design, css-101.org and ez-css.org. His twitter handle is @thierrykoblentz.

Advertising
  1. 1

    Waoo this look great, never knew many of these techniques before :|
    Thanku for sharing this…

    0
  2. 6

    I’m just giving it a try on a Mac right now. It’s so great people are working on inventing new techniques all the time.

    0
  3. 7

    Nicolas Gallagher

    March 19, 2011 12:14 pm

    Very interesting article Thierry.

    Your idea of using CSS expressions will surely appeal to those who aren’t prepared, able, or allowed to serve simpler sites to IE 6/7. But it’s worth noting that inserting new elements into the DOM may lead to cases when the :first-child selector will not work as expected in IE 7.

    I experimented with clip, but the requirement for absolute positioning can be limiting. For high contrast environments, I don’t think losing a decorative icon is problematic if the associated text remains visible. But for images that are more essential or no text redundancy – e.g. logos, pagination prev/next buttons, social networking icons – another method is to use url() and negative margins (e.g. http://nicolasgallagher.com/css-image-replacement-with-pseudo-elements/).

    Thanks for sharing your great work!

    0
    • 8

      Thierry Koblentz

      March 19, 2011 3:46 pm

      Hi Nicolas,

      Thank you for the heads up regarding “:first-child” and IE7.

      I’ve checked the page you link to, but I’m not sure about the fallback mechanism for IE 6 and IE 7. It appears that it uses negative text-indent which is an issue with images off.
      I’ve been using a slightly different technique myself [1]. The method relies on img elements which give similar results across browsers.

      Note that this is an old post. Today, I’d mix that technique with the one explained in this article, to avoid the “junk” markup.

      Thanks a lot for your feedback and the inspiration!

      [1] http://tjkdesign.com/articles/how-to_use_sprites_with_my_Image_Replacement_technique.asp
      (the newest version relies on position:relative instead of position:absolute)

      0
      • 9

        Nicolas Gallagher

        March 20, 2011 3:16 am

        Yeah the fallback was an existing image-replacement technique, since IE 6/7 don’t support pseudo-elements and can’t take advantage of the NIR technique. While I don’t use CSS expressions, they could be used to add the image to the DOM in IE 6/7 (as you described above) for the purposes of IR. Means you can avoid using clip.

        0
        • 10

          Thierry Koblentz

          March 20, 2011 9:02 am

          We could do without clip, but then we’d need to plug two elements instead of one (the img with a wrapper). This is because we need to be able to crop the sprite.

          With a background-image technique we’d need only one element (which could be a img).

          0
          • 11

            Nicolas Gallagher

            March 20, 2011 11:01 am

            For IR, the element used to crop the sprite is already there in the DOM. It’s the same one used to crop a sprite inserted using a pseudo-element and content:url(). For decorative icons that sit alongside text, I don’t think it is worth the trouble of showing them in print / HC because they aren’t necessary to understand or use the site…so background-image does the trick.

            0
  4. 12

    This is great, although falling back on expression() for IE is a bit dangerous if you mind about performance… You might want to take a look at this: http://blog.dynatrace.com/2010/02/16/the-real-performance-overhead-of-css-expressions/

    0
    • 13

      Thierry Koblentz

      March 19, 2011 3:08 pm

      Hi Charles,
      As I mention in the article, this is a different type of CSS expression than the ones you refer to. These are not as bad since they are evaluated only once. If it was not the case, then you’d see more than a single icon/glyph per item…
      Thanks for joining the discussion.

      0
  5. 14

    I love this!

    0
  6. 15

    Awesome insight. Thanks Thierry. Pushing the envelope – way to go!

    0
  7. 16

    Thierry Koblentz

    March 19, 2011 2:59 pm

    @Aamir, @Jakub, @Sereal, @Sascha – Thanks for the kind words/feedback

    0
  8. 17

    I’m not getting this. It seems like you’re saying the left value in background position is actually calculated from the right in RTL contexts. So why not align the images to the centre if that’s an issue? And I would assume the keyword ‘left’ aligns it left whatever the context.

    Regardless I don’t think RTL differences would be an issue for most people: if the page is written in a LTR language, it’s usually intended to be read that way.

    0
    • 18

      Thierry Koblentz

      March 19, 2011 8:51 pm

      Hi Scott.

      You’re correct, the keyword left positions the background image to the left of the box, regardless of context (LTR or RTL).

      But to position the image on the other side of the box, one needs to swap things horizontally . For example, using the two sprites (LTR/RTL) given above, one would have to change the declaration from:
      background-position: left top
      to:
      background-position: right top
      For the first image in the sprite to appear in the “right” corner of the box (no pun intended).

      If the images were aligned in the center of the sprite we would need to compensate their offset from the edge of the sprite with negative values to bring them close to the edge of the box. This may allow the use of a single sprite, but at the same time it would seriously increase the complexity of the background-position declarations. This is because each image inside the sprite would need custom negative values (to accommodate both LTR/RTL contexts).

      And yes, you’re right saying that most people do not have to care for both contexts.

      Thanks for your feedback!

      0
  9. 19

    Dear Smashing Magazine team , why you don’t do some video tutorials from time to time !!
    i believe its more details than text tutorials .
    thanks .

    0
    • 20

      Smashing Media Customer Support

      March 20, 2011 3:08 am

      Thank you very much for your suggestion. We consider every single suggestion we receive, and we are constantly looking for advice for improvement and helpful tips from our readers.

      0
      • 21

        Yes, that’s right. Provide video contents but please also put text tutorials as alternate content.

        0
    • 22

      Michel Bozgounov

      March 21, 2011 1:04 am

      Video tutorials are hard to follow sometimes. You can’t easily read them in an office, for example, since you’ll have to be with headphones.

      Reading texts is much easier.

      Also, when there is code involved, much better is to follow text and code examples. This would not work if it was a video… :)

      @Smashing Mag:

      If you make video tutorials, please make sure there is also a plain text/images version available! :)

      0
  10. 23

    Icons:
    Does anyone know where I can find those little 16x16px looking browser icons used in the first image figure?

    0
  11. 26

    Gareth Partridge

    March 20, 2011 5:18 am

    Great stuff, lots to play around with here, thank you for sharing.

    0
  12. 27

    Very informative and up to date. Loved it and definitely gonna consider it. Cheers

    0
  13. 28

    im not much of a developer, but I really like the visuals these little additions can give to a website. I look forward to sharing this article with my team :)

    Thanks!

    0
  14. 29

    Long time no hear Thierry!!!
    Awesome to see someone from the Drumbeat past becoming famous :)
    Loved the article, gives me lots of ideas for current project.

    Take care and keep up the great work :)

    Jaana
    from Drumbeat 2000 past :)

    0
    • 30

      Thierry Koblentz

      March 20, 2011 1:49 pm

      Hey Jaana,

      Drumbeat 2000! Wow!
      Those were the days, isn’t? :)

      Hope all is well on your end. Thanks for stopping by! :)

      0
  15. 31

    Hey, nice article!! Short but sweet and something to learn!!

    I use the background sprites a lot but as you mentioned, I had to stick to either LtoR or RtoL! I’m sure gonna try the clip method!!

    Also, the special characters! I’ve known them before! Thanks ! ♠, ♣, ♥, ♦

    0
  16. 32

    Michel Bozgounov

    March 21, 2011 1:07 am

    Excellent article!

    Not too long, not too short, and describing a clever CSS/CSS3 technique! My thanks to the author — you sure gave me some food for thought and I’ll be experimenting with some of these ideas very shortly, maybe even in my next project! ;)

    0
  17. 34

    Hy guys,

    have u ever tried to test the code of this article in IE<9.
    it's funny, because of the expression in the css part friezes the ie.

    the expression executes every time an events is fired.

    found in the part "Starting with special characters".

    0
    • 35

      Thierry Koblentz

      March 21, 2011 9:37 am

      @Patrick

      You are talking about IE<9, but as far as I know IE8 does not support CSS expressions (dynamics properties). See: http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx

      Also, how do you test your pages? Using IETester, compatibility mode in IE8 or 9 etc. or the real thing? I have a test page that IE6 in VPC loads just fine. It contains over 1,000 items styled with these CSS expressions. You can check it here:
      http://tjkdesign.com/test/ie-expressions.html

      the expression executes every time an events is fired.

      Don’t you think if it was the case there would be more than one element plugged in each node?

      Thanks for joining the discussion.

      0
      • 36

        I’m having the same problem with IE7 (IE9 compatibility mode). It just runs the expressions hundreds of times and freeze.
        Any tips to avoid the multiple executions?

        0
  18. 37

    Nice article! I just tested it and as it turned out IE 8 (Win xp) needs the wrapper element to be block level. So apply display: block on links etc. Otherwise the :before-Element will be displayed behind the wrapper element.

    0
    • 38

      Thierry Koblentz

      March 21, 2011 9:39 am

      Hi Bernd,

      I cannot reproduce your issue, do you have a test-case I could look at?

      Thanks for your feedback

      0
      • 39

        I’ve send you a test case to your css-101.org mail address with a example HTML5 document and a screenshot.

        0
        • 40

          Thierry Koblentz

          March 21, 2011 3:59 pm

          Hi Bernd,

          I’ve checked your document. It uses a very different construct – you are using anchors inside paragraphs so the normal implementation would be to style the paragraphs instead of the links which would solve the problem right there ;-)

          In any case, your page shows that IE8 paints the background of your anchors over the image (that’s a bug). To fix this you may want to use “display:inline-block” instead of “display:block” so the links behave more as you’d expect.

          Thanks for sending me your test case!

          0
          • 41

            Well, but isn’t it better to make icons clickable and being able to have multiple buttons (with icons) in one paragraph or div? I think inline-block should be fine for that. Thanks and let me say it again: Nice article!

            0
  19. 42

    Jorgen Kesseler

    March 21, 2011 5:50 am

    I really like how this technique sovles the issues that come with traditional background images but I really hate IE’s CSS expressions and avoid them like like the plague.

    0
  20. 43

    The idea of running any production code that uses -ms code fills me with dread. I think it is better in all circumstances to avoid those kinds of solutions as they’re targetting 1 particular problem, although yes I concede the fact it works across all IE’s is a positive.

    It’s a good experiment for sure.

    0
  21. 44

    I tested the code with IE7 and IE 8 on WinXP Pro and it works just fine. Nice one. Thank you.

    0
  22. 46

    Sorry for the double posting but for those who are interested, check this out:
    http://jrgraphix.net/r/Unicode/2190-21FF
    This is the unicode range for arrows, dingbats are also quite nice.

    For IE just use:
    .sevas,
    .sevas:before {
    content: “27b5″; /* unicode */
    background-image: expression(this.runtimeStyle.backgroundImage=”none”,this.innerHTML = ‘‘+this.innerHTML);
    }

    0
  23. 47

    ups, I typed the full code so on screen the arrows is already displayed. You need to write the ampersand (&) and the character glyph: #10165;

    0
  24. 48

    Ummm, since I read this in Google Reader which did not render the glyphs (the list was just a list without any markers) I’m hesitant to get too excited. I only knew from the context that something was missing…

    0
  25. 49

    Very nice thing, but can this be – and should this be – used to store the whole UI in one png file, like on fox.com? (http://www.fox.com/_ui/images/css_sprites/sprites.png)

    Is this some alternate way to style all the buttons, form items, etc. with one sprites.png, or just to insert some icons?

    0
    • 50

      Thierry Koblentz

      March 21, 2011 2:00 pm

      Hi Keriati,

      The short answer: yes (and one could use a sprite with much less white space than the one you link to).
      The long answer: it depends on what one wants to achieve, the construct etc.

      In other words, this is just an extra tool in the box. It is up to the author to find out which technique is best to implement to solve a given problem.

      0
  26. 51

    Hi Thierry,

    thanks for your article, very useful…
    I tested the part of the special characters for the list.

    On chrome and firefox: ok
    But with opera10.63 for linux the glyphs are double and the phrase does not appear in the list

    See you

    0
  27. 54

    A pletora of glyphs! Wait… ?

    Typo – it should be “plethora”.

    0
  28. 56

    The clip method is perhaps the most usable. I have to change my mind about the icons loaded via CSS. I’t a several years practice but of course have some issues and it is hard with sprites.

    Anyboady agree? I would like to read some more opinions.

    0
  29. 57

    Due to Operas support for CSS3 generated content module your grouping of the .one,.one:before selectors will break in Opera and any other browser that fully supports the generated content module.

    With the CSS3 generated content module if you apply the content property[1] to an element, rather than a pseudo-element, it will replace the current content within it, hence why your example list comes up with two glyphs symbols rather than the content in Opera (it’s correct behaviour and not a bug). This will need to be addressed otherwise once browsers fully support this module your technique will break badly.

    [1] http://www.w3.org/TR/css3-content/#inserting3

    0
  30. 59

    Implemented this bad boy (using the cool glyph method) on a site in dev. Works like a champ. Great tutorial. Many thanks.

    0
  31. 60

    I made sprites with 3px margins between elements, because IE6 often shown 1px from another image.

    0
  32. 61

    Using expressions hangs up IE9 in IE7 browser mode. I didn’t check it in real IE7 but it seems expressions affect performance greatly

    0
  33. 62

    When I try to apply the pseudo code for styling “more” links, IE7 starts repeating the “generated” html defined in background-image nonstop. I tried adding background-repeat: no-repeat, but it has no effect. How do you stop this?

    0
  34. 63

    Thx for the article!

    Could you maybe explain how does this work in IE 8? Will IE = 8 use the pseudomethod :before method? Here http://caniuse.com/#search=clip it says IE 8 does not understand the css clip expresion (:before looks fine though http://caniuse.com/#search=before). So how does IE 8 scale the image?

    Will IE >= 8 ignore the css expressions or should I target them only to earlier IE browser versions?

    Also for the “THE NEW URL() / CLIP METHOD” () in my Chrome Version 21.0.1180.89 the image has 2 pixels missing on the bottom, only when I make clip: rect(0 30px 17px 15px) does it show all of the image.

    0

↑ Back to top