New(?) pure CSS image replacement technique JIR

Date: 07 Mar, 2009
Posted by: admin
In: design, fonts & graphics|internet, web design & development|Search Engines, SEO & SEM

Bookmark and Share

My review of IR methods claimed LIR as the winner a fundamental link flaw lead to further considerations. Thus I present my CSS image replacement method, JIR, which uses CSS2 :before pseudo-element to insert a replacement image in place of, for example, header text. It allows transparent images, displays a header with CSS and/or image off, uses proper semantic code with no extra elements, presents text to search engines, is easy to do, doesn’t need any assistive technology (eg js,php). If this sounds too good to be true, and your a web developer, you’ll already know the gotcha! But there is still light, and a useful advantage, at the end of the post. Read on …

Problems with other methods

In an article by Paul Young, Sitepoint 9 Feb 2009, he makes the following claim about CSS image replacement, which, up until today (following my review) I was in total agreement with:

Most other image replacement techniques (Fahrner, Phark, Gilder/Levin, Leahy/Langridge) use the same CSS when images are on and off. I’ve come to the conclusion that it’s important to make use of different CSS when images are on and off, otherwise the result suffers from one or more of the following side effects:

* The technique breaks easily—for example, when images are disabled or if the replaced image has transparent regions.
* It’s too complex to be implemented rapidly and reliably.
* The technique uses JavaScript to traverse the DOM tree, which causes an undesirable flicker while the page loads (and is brittle for a variety of other reasons).
* The technique may be incompatible with some browsers.

Desirable outcomes for an image replacement method for me then are that:

  • the technique works with transparent regions (e.g. PNG transparency), [PNG]
  • when images are off a stylable text is shown, [images=off]
  • when stylesheets are disabled an unstyled title is shown, [CSS=off]
  • resizing with page or text zoom doesn’t break the technique [zoom]
  • the replacement image can be a link, and it’s whole area is clickable [link]
  • no extra elements (e.g. empty spans) are needed, good semantic markup only
  • no assistive technology is needed, JavaScript, PHP, whatever
  • the method works across all modern browsers with a 1%+ market share

The method I present here hits 8 out of 9 of these requirements.

This was what I was hoping to find in my review. I found the best method to be Leahy-Langridge (LIR) but it doesn’t include, in the presentations I saw, the ability to set the header up as a link. In my review I’d managed to overlook this important detail until I came to set my WordPress blog up to use LIR and found the H1 logo was no longer linked properly (if you’re pernicious you can find it with some mouse waving).

So, what now.

Example implementations

7 image replacement methods

7 image replacement methods

First I implemented all the methods of the review in an example page with examples of LIR, TIP, GLP, Phark, Position-Absolute and Fahrner image replacement as well as a first stab at a better IR, called LLJ, which is basically LIR with an anchor tag stuck in.

A warning: I can’t guarantee these implementations are correct, I may have missed some nuance or other that renders one of these a better IR than I’m presenting here. Please comment if you spot an error anywhere.

How are the examples deficient?

[There’s a nice table of this below including my test methods and JIR tallied against the desirable outcomes.]

Well clearly (see image) TIP, GLP and Position-Absolute have overlay problems with transparent replacement images, but these are the only ones that present the images as clickable links. Altering LIR by moving the technique of hiding the image in the padding from the H2 to the A tag, a sort of mix of LIR and TIP allows the replacement image to be clickable.

LLJ wins?

Not quite time for tea and crumpets I’m afraid. This LLJ method has serious problems still. CSS = off works, but Images = off presents no heading and certainly not a clickable one. This is, in my opinion, a very servicable method. If you have same height headers and bump the common code to a class you only have to set the background-image value for each replaced header (which I argue later can be done in the HTML content).

<h2 id=”llj” class=”llj”>
<a href=”” title=”LLS adjusted”>Leahy-Langridge-Jefferies method 1</a>

h2.llj {
margin-top: 15px; /* for this particular site, set this as you like */
position: relative; /* allows child element to be placed positioned wrt this one */
overflow:hidden; /* don’t let content leak beyond the header – not needed as height of anchor will cover whole header */
padding: 0; /* needed to counter the reset/default styles */
h2.llj a {
position: absolute; /* defaults to top:0, left:0 and so these can be left out */
height: 0; /* hiding text, prevent it peaking out */
width: 100%; /* 686px; fill the parent element */
background-position: left top;
background-repeat: no-repeat;

h2#llj {
height: 60px; /* height of replacement image */
h2#llj a {
padding-top: 60px; /* height of the replacement image */
background-image: url(“images/replacement/llj.png”); /* the replacement image */

Further testing and examples

JIR example / test implementation

JIR example / test implementation

From here I tried several changes which were all successful to some extent.

Those alterations and the final JIR technique are shown in the JIR example file, which includes the previous tests, most recent additions are at the top of the examples, I’ll look at them in reverse order here.

  • LLJ – uses the anchor-tag to cover the H2 and provide the replacement image in the background of top-padding.
  • LLJ2 – took the replacement image out of the background and placed it in an image tag in the header, the image and the header are the same size and so, with overflow:hidden, the anchor simply gets pushed out of view. The anchor no longer works. Images = off gives the unclickable image alt text alongside a stylable clickable header. Images + CSS = off gives the same, but sans styles. CSS = off however messes it all up by giving us the header image and the header text side-by-side. It looks bad, keep trying!
  • LLJ3 – we move the image inside the anchor, to make a clickable header image as the default presentation. We’re nearly there it seems, but as with LLJ2 we still have a double header when CSS is off.

This leads to the natural thought of what can make the image appear when CSS is on and disappear when it’s off? The CSS2 pseudo-elements :before and :after, can they do it?!

Introducing JIR

[If you’ve seen this method elsewhere, please let me know, I couldn’t find it when I looked before writing this up. Some posts are close.]

My eponymous IR, JIR, then uses base HTML like so (note that the span is only to style the title, it is not needed for this IR method):

<h2 id=”jir” class=”jir”>
<a href=”” title=”JIR method”>LLJ4 / <span style=”color:red”>Jefferies</span> IR method</a>

With CSS as follows (see JIR example page for live version you can test, I used the  webdeveloper add-on for FF testing):

.jir {
position: relative; /* allows child element to be placed positioned wrt this one */
overflow:hidden; /* don’t let content leak beyond the header – not needed as height of anchor will cover whole header */
padding: 0; /* needed to counter the reset/default styles */
.jir a {
position: absolute; /* defaults to top:0, left:0 and so these can be left out */
width: 100%; /* fill the parent element */

h2#jir, h2#jir a {
margin-top: 15px; /* for this particular site, set this as you like */
height: 60px; width:632px; /* dimensions of replacement image */
h2#jir a::before {
content: url(“images/replacement/jir.png”);

The comments there (or in the test CSS file) should be enough to track what’s happening.

Summary and review

Checking now against our desired outcomes, T indicates text shown, I indicate image shown, C indicates linked/clickable:

desired outcomes JIR LLJ3 LLJ2 LLJ LIR+S TIP GLP Phark Pos-Abs Fahrner
PNG Yes Yes Yes Yes Yes No No Yes No Yes
images = off CT CT T CT T T T
CSS & img = off CT CT CT T CT CT CT T T CT T CT
zoom Yes Yes Yes Yes Yes No Yes Yes Yes Yes
link Yes Yes No Yes No Yes Yes No Yes No
HTML Good Good Good Good Good empty anchor empty anchor Good Good span
browser support MSIE fail
[Score] 7/8 6/8 4/8 7/8 6/8 2/8 3/8 6/8 4/8 5/8

We actually see that it’s quite close. Now in my opinion linking the header at all times and not displaying a double header if CSS = off are essential. This leaves us with 2 options. Either the JIR method, or the adaptation of LIR+S with clickable anchors that I’ve called LLJ.

Browser compatibility of JIR method

Well, you can tell me. I’ve tested using Virtualbox WinXP and Kubuntu Linux natively and tested visually using browsershots. Browsers tested:

  • Mac OSX (visual), Win XP and Linux: include Opera , Firefox, Konqueror, Safari, Chrome
  • Win XP: IE5.5, IE6, IE8 beta 2, IE8 beta 2  as IE7

Bet you can tell me the results. This JIR method works everywhere except MSIE. It doesn’t matter which version of MSIE either, all of them fail right up to IE8 beta2 (in my testing), even when I used Dean Edwards IE7/IE8 compatibility JavaScript in conditional comments on the testing page (commented out at present).

This is not really unexpected, no not because MSIE is rubbish (!) but because it doesn’t support the :before and :after pseudo-classes as noted by Peter-Paul Koch at in his support grid of CSS2 selectors. Elsewhere you can find notes concerning the lack of support for the CSS3 pseudo-elements which now use double-colon notation – ::before and ::after (MSIE doesn’t support any pseudo-elements).

End of the [short] road for JIR?

You made me read all that and then tell me it won’t work? No.

Well, yes and no.

MSIE users still get a styled header, so you could combine this technique with a simple CSS style for IE, or possibly use the IE’s support for downloading EOT fonts (or maybe not). But realistically until MSIE is supporting these pseudo-elements then JIR is not going to be sweeping the web. I hope I’ve shown here that JIR is a positive method that is more accessible, more user friendly, better SEO-wise and better graphically (allows transparency) only let down by browser support.


The winner is LLJ

The most common implementation of image replacement across the web appears to be FIR. FIR is seriously lacking and many designers are using the Leahy-Langridge method (LIR). But this too in its default does not include a full sized link element. LLJ keeps the benefits of LIR and adds clickability, keeps semantic HTML and avoids showing a double header when CSS is disabled. LLJ is light-weight only requiring the setting of a per-element background-image over a class. My recommendation therefore is LLJ. It evens works in IE6+ (transparency problems aside, consider sending IE gifs).

Thanks for stopping by please feel free to comment or ask questions.

For my next foray into CSS image replacement I intend to extend LLJ to implement sprites.

Useful post? Go on, buy me a treat I promise I'll share!
— recommended donation £2 / $3 / €2.5

18 Responses to "New(?) pure CSS image replacement technique JIR"

[…] a post after a couple of days research – I’ve sorted the problems with LIR and present a clickable transparent image based, pure semantic CSS image replacement technique (lots of examples in […]

[…] New(?) pure CSS image replacement technique JIR | flapjacktastic […]

Monkeytail says:

Thanks for the methods!

I tried the LLJ method. In Firefox there is some 20px whitespace above the img.. can’t get it away :S

Monkeytail says:

^ solved

admin says:

presumably you’d not adjusted the height for the image you’ve chosen? glad you solved it,

PS: that looks like a pig’s tail, what gives

Kris says:


Please add your site at is a place where other people can find you among the best sites on the internet!
Its just started and we are collecting the best found on the net! We will be delighted to have you in the sweebs listings.


mari says:

Couldn’t you just use display:block and get rid of the relative/absolute positioning? The code is much lighter just like this:

h2#llj a {
padding-top: 60px; /* height of the replacement image */
margin: 0px;
height: 0px;
display: block;
overflow: hidden;
background: url(“images/llj.png”) no-repeat left top;

no need to really make a declaration for h2#llj unless you need to position/style it further.

It seems to work for me.. and btw, your code uses left bottom for the image which positions it weird in IE 7.. so i changed that to left top.

Also. It might not be obvious to everyone, this doesn’t work in IE w/o a valid doctype declaration of HTML 4 or XHTML 1.

Also.. what is the purpose in using both ID and class attributes? It just seems to clutter the code. I’d much prefer a method that uses one declaration block per heading. (And if you are replacing an H1, there is no need to pool shared attributes in class because there should only ever be one H1 per page.)

admin says:

Mari, thanks for your input. Yes display:block works for non-positioned headers in FF, haven’t tested elsewhere.

Will have to check out the left-bottom issue as, because the height is declared, this shouldn’t be different.

Hadn’t tested without a valid doctype, I always attempt to be standards compliant.

ID and class is to reduce the repetition of code when doing multiple headings. If you keep a fixed heading height then each new heading would only need a background image attribute to be declared.

mari says:

I mentioned the doctype thing cuz I was testing in a shell html file that I hand coded with no doctype and comparing it to your html that I downloaded and was going crazy because one was working and the other wasn’t.

As for the left bottom, it affects both LLJ and LLS (and classic LIR). I assume it has something to do with the padding-top value.. maybe an IE bug?

mari says:

Actually. Sorry to keep posting. But I just figured that left-bottom thing out. IE 7 is positioning the image from the bottom left of the hidden overflow. So if you change the font-size you can see it shift up because the overflow gets smaller. But even with a font-size of 0px, it won’t be positioned perfectly and you’ll lose the readability when you have images off and css on.

mari says:

Hi. Me again. I think you should consider taking LLJ3 out of the doghouse. Although I hate throwing images inside H* tags, it does something none of the other methods can… it allows for dynamic generation of images to be used as the headings. All the other methods require static images in order to be coded into external CSS files. Otherwise, you would have to write the css background property in the document head or inline to use a dynamic image. (One site that uses a dynamic script/widget for their headings is, although they aren’t implementing the browser text.)

(I use sIFR 3 for dynamic headings, but some large companies and sites don’t believe sIFR is stable enough for their use.)

I honestly don’t think the double header with css & images off is a huge issue. Most sites will break into pieces with images and css off anyway. The images off/css on discrepancy is probably more important, because the headings, both image and text, are missing completely!

Also. I know it is not semantically correct. But LIR and Phark can have clickable headings if you wrap the H* with an A tag.. but your HTML won’t validate then, but it is a quick and dirty fix.

admin says:

The css and images off issue is certainly not huge but I was trying to get as pure a solution as possible. Thanks for your input on this – I’ve been busy with other work over the Summer, but along with Fox’s post perhaps it’s time to re-review and get on with the sprites too.

Fox says:

Hi. I note that you intend to extend LLJ to implement sprites. I’ve just come across Eric Watson’s new ‘Spriteric Image Replacement’ technique (published July 09) at Apparently the only sprite navigation menu that works with CSS On/Images Off with :focus and :active pseudo-classes. Thought it might be of interest…

admin says:

@Fox, thanks for the suggestion … I’ll get around to it when I have some free time … like, never

Dave says:

What does CT stand for in the results table?

Dave says:

All good, I somehow missed that C = clickable

Christo says:

Excellent. Best resource I’ve seen on this matter so far.

admin says:

much obliged, thanks for your comment


Flapjacktastic is just a random collection of musings, hints&tips, notes, information ... a collection of stuff really that's overflowed from the brain of this husband, father, potter, business-man, geek ...

past posts