Styling Elements With Glyphs, Sprites and Pseudo-Elements
- By Thierry Koblentz
- March 19th, 2011
- 63 Comments
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.

Starting with special characters Link
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: Link
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? Link
- 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
::beforenor: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
:beforeand::before, or between:afterand::after. The single colon syntax (e.g.:beforeor: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 Link
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:
![]()
The two images below — which are the second icon in the sprite — are generated using each technique, respectively.
Nicolas Gallagher’s method Link
- 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; } - Using the
contentproperty to insert the sprite which is then cropped withclip: -
#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 */ }
The new url() / clip method Link
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? Link
- Instead of styling the pseudo-element with a background, we use it to insert an image (via
content). - Using the
clipproperty, 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
clipvalues by using theleftand/ortopproperties.
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:

Advantages of this method over existing techniques Link
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 Link
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:
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 Link
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 Link
You might want to take a look at the following related resources:
- CSS3 module: Lists – The ::marker pseudo-element16
- Generated content, automatic numbering, and lists17
- The clip property18
- Colour Contrast and CSS Sprite Maps19
- High Contrast Proof CSS Sprites20
Credits: Icons by FatCow Web Hosting21 [CC-BY-3.0-us], via Wikimedia Commons22
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(https://media-mediatemple.netdna-ssl.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(https://media-mediatemple.netdna-ssl.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 Link
- 1 http://www.alistapart.com/articles/taminglists/
- 2 http://nicolasgallagher.com/css-background-image-hacks/demo/crop.html
- 3 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
- 4 http://inamidst.com/stuff/unidata/
- 5 http://www.fileformat.info/info/unicode/char/2666/index.htm
- 6 http://www.w3.org/TR/html40/sgml/entities.html
- 7 http://www.impressivewebs.com/before-after-css3/
- 8 http://robertnyman.com/2007/11/13/stop-using-poor-performance-css-expressions-use-javascript-instead/
- 9 http://www.artzstudio.com/2010/04/img-sprites-high-contrast/
- 10 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
- 11 http://en.wikipedia.org/wiki/Data_URI_scheme
- 12 http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/
- 13 http://nicolasgallagher.com/css-background-image-hacks/
- 14 #
- 15 http://lists.w3.org/Archives/Public/www-style/2010Nov/0437.html
- 16 http://www.w3.org/TR/2002/WD-css3-lists-20021107/#markers
- 17 http://www.w3.org/TR/CSS2/generate.html
- 18 http://www.w3.org/TR/CSS21/visufx.html#clipping
- 19 http://juicystudio.com/article/colour-contrast-css-sprite-maps.php
- 20 http://www.paciellogroup.com/blog/2010/01/high-contrast-proof-css-sprites/
- 21 http://www.fatcow.com/free-icons/
- 22 'http://commons.wikimedia.org/wiki/File:Farm-Fresh_bullet_feed.png'

Hold on tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Oxford, on March 15—16, with smart design patterns and front-end techniques.
