CSS 3 Image Replacement

Since before the dawn of time, designers have wanted sexy fonts or images for headings, links and buttons on web pages. The adoption of @font-face for CSS to use any font a designer specifies will take away some of the use cases. The time-honoured method of using img in the source code, and using the alt attribute to as a text alternative still works, of course, and very nice it is too, but some designers prefer to use CSS to replace text with an image.

There is a variety of image replacement techniques, but they all add a background image and then hide the text, usually by moving off the screen. They all suffer from restrictions: they can only be used with opaque images, or against flat colour, or they need extra spans for style hooks. There's also the problem of people who surf with images off, which is quite common for those using phones whose web use is metered. They don't see the image, but don't see the text either.

Using the content property

The CSS 3 content property is experimentally supported by Opera and Safari. Firefox supports the CSS 2.1 spec when used with the :before and :after pseudo-elements and there is rudimentary support (so far) by IE 8 beta.

It allows you to "insert" text or an image into an element, over-writing the HTML content of the element.

So, for example, this code snippet will replace my name in the h1 with my blog logo:

h1 {content: url(bruce-logo.jpg);}
<h1>Bruce Lawson’s gorgeous blog</h1>

You can see it in action on this image replacement test page.

It's interesting to note how the browsers style this differently. To centre the logo, you need to style the h1 with text-align:center, as if Opera centres the real text and then replaces it with the image. Safari will only centre the logo with margins, as if it replaces the text with a virtual img tag, which is then impervious to text-align.

What should browsers do if the image can't be shown?

I think, in any circumstance that a browser can't show an image, it should show the content of the element in the natural position for that text. That would ensure that this CSS 3 image replacement technique degrades well when CSS and images are off.

This behaviour would be similar to iframe: "The information to be inserted inline is designated by the src attribute of this element. The contents of the iframe element, on the other hand, should only be displayed by user agents that do not support frames or are configured not to display frames." In a similar way, html has a fallback mechanism for images and objects: when it can't show the image, it shows the alternate text.

Here's where the support for this image replacement mechanism gets experimental, even in the highly standards-compliant browsers. This is because the specification changed between CSS 2.1 and CSS 3.

CSS 2.1 said

If the user agent cannot display the resource it must either leave it out as if it were not specified or display some indication that the resource cannot be displayed. CSS2 spec

So the current "standard" says that it should either show the contents of the element (the actual text between the tags), which makes sense to me, or some sort of "image not found" icon, which seems to me to be the worst of all worlds. If a screenreader or a search robot gets the plain text, why not a user who has images switched off?

The CSS 3 spec allows you to set a series of fallbacks, much like when you set font-family. So here, the browser will try to replace the header with a movie, then an animated gif if it can't find or display the movie, or a standard jpg if it can't find or display that gif, and if it can't render any of those, it will just show the contents of the element:

h1 {content: url(header.ogv), url(header.gif), url (header.jpg), contents; }

The spec says

If no alternatives exist, then 'none' is used as a final fallback...Thus to make an element fallback on its contents, you have to explicitly give 'contents' as a fallback. CSS 3 spec

To me, it's odd that anyone should want to display nothing instead of the content, let alone make it the default, but at least the new specification allows you to unambiguously tell the browser to display the contents of the element: it's not allowed to "choose" between the text and a "image not found" icon.

What do browsers do?

However, browser support is still experimental as the generated and replaced content module of CSS 3 is still susceptible to change. My no-images test page shows that Opera and Safari still have problems following the CSS 3 spec fully.

Conclusion

Once the CSS 3 spec is finalised and the browser support is solid, a foolproof way of doing image replacement will be available to us that is accessible to screenreaders and which degrades gracefully with CSS off and with images off.