Styling Elements With Glyphs, Sprites and Pseudo-Elements

This article was first published on smashingmagazine.com (03-19-2011).

In 2002, Mark Newhouse published the article “Taming Lists”, 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-crop 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 sprite technique. The technique will work in Internet Explorer 6/7 as well.

Displaying icons in links and as custom list markers. Displaying icons in links and as custom list markers.

Starting with special characters

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

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?

Note that the CSS expressions we use here are not as bad as the ones generally used to mimic min-width 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></s> and Facebook uses empty <i></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 following examples do not use extra markup and they both share the same sprite:

Sprite The sprite.

The images — 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?

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 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 mode or with high-contrast styles sheets.
Styled to work in IE lt 8
This method works in Internet Explorer 6 and 7 as well.

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

Nicolas Gallager shows plenty of cool stuff 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 more

CSS:

.more:after {
    white-space:nowrap;
    content: " \00BB"; /* » */
}
.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-readers, 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 Hosting [CC-BY-3.0-us], via Wikimedia Commons