Similar to the FIR technique, this CSS technique allows decorative images to replace text found in headers. For an argument of why this is useful, and a description of FIR itself, see this page.
What GIR does that FIR and other alternatives don't, however, is overcome the browser scenario where images are turned off for some various reason, yet CSS rendering remains active, without using any excess markup. Also, since no content is actually "hidden" per se, screenreaders should have no difficulty correctly reading out the header text.
Anybody that can verify support, or remove platform/browsers from the Unknown list would be much appreciated. Please email me with your test results, and I'll be sure to credit you with a link on this page.
I would like to thank Brothercake and Steve Potts (from Zendor) for the extensive browser testing they have performed, as well as their suggestions. The above lists would be practically empty if it wasn't for them - especially if Brothercake didn't have every browser/platform combination known to man!
CSS:
.gir {
position: relative;
margin-top: 0;
margin-bottom: 0;
padding: 0;
}
@media screen { .gir { overflow: hidden; } }
.gir:after {
content: "";
background-repeat: no-repeat;
position: absolute;
left: 0;
top: 0;
}
* html .gir {
background-image: expression(this.innerHTML += '<div class="gir"></div>', this.runtimeStyle.backgroundImage = "none");
}
* html div.gir {
background-image: none;
background-repeat: no-repeat;
position: absolute;
left: 0;
top: 0;
}
.gir[class*="gir"]:before {
display: block;
}
.gir[class*="gir"]:after {
display: none;
}
#fancy {
/* Declare height of replacement image */
height: 59px;
/* Style the header text in case images are disabled */
font: bold large Arial,Helvetica,sans-serif;
}
#fancy:after, #fancy div.gir {
/* Declare the image source, as well as dimensions */
background-image: url(fancyheader.gif);
width: 217px;
height: 59px;
}
#fancy[class*="gir"]:before {
/* Declare the image source */
content: url(fancyheader.gif);
}
The markup:
<h1 class="gir" id="fancy">Fancy Header</h1>
First note, I divided the CSS up into 2 sections. For multiple headers, this factors out some of the properties you would have to redeclare, and puts them into the class gir. We'll look at the CSS on a rule-by-rule basis now.
The real important thing going on here is the relative positioning of the header element. This allows us to absolutely position children, which Opera and IE both rely upon.
This was originally a part of rule #1, however for the effect to fail gracefully in IE5/Mac, the overflow property had to be hidden from it.
This is the rule for Opera. Against the W3C CSS2 specifications, Opera allows generated content to be position. So we simply position the generated content above the text. The corresponding #fancy rule sets the background-image of the content.
This is the big rule for IE. It's quite a hack, in case you couldn't tell. This was originally done with a DHTML behavior, but there were slight complications associated with that method.
Brothercake suggested using the expression() value available in IE, which had promise. Despite a lack of documentation on MSDN, I found that this referred to the selected element in the scope of the expression. Now, the ultimate goal is to inject a box that will be positioned over the text, just like in Opera. This is done with innerHTML and a <DIV>. An important thing to note, however, is that expression() is recalculated everytime something that affects layout is performed. Inserting markup affects layout. So what happens when you insert markup inside the expression()? An infinite loop in the renderer, followed by a crash. So how can this be overcome?
Enter a technique I discovered out of necessity, which I call "one-time use expression()". It relies upon the rarely used comma operator in ECMAScript. The expression expr1, expr2 evaluates both expressions, but returns only the value of the second. So we do 2 things inside expression() using this. The first being our dirty deed, secretly inserting markup into the header. The second removes the mechanism we utilized itself! We simply set the value of the property to "none", overriding the current expression() value. It goes away, but leaves the inserted markup. This hack effectively allows "CSS" (I'll use the term loosely) content-generation in IE4+ in Windows! The limitation being, of course, that one must have scripting enabled. Some have told me that expression() works with scripting disabled (that would be a serious security issue in IE, which wouldn't be surprising in the least), however my tests seem to indicate otherwise.
This is the cleanup and preparation rule for IE. First, reset the background-image from the expression() so it doesn't create an infinite loop in the renderer, then positions the generated <DIV> over the text.
This is for Mozilla and Safari. The idea behind this method is that Mozilla doesn't generate boxes for content-generated images when images are turned off. Therefore, we simply insert the image directly with content: in the #fancy rule. Very, very simple. We set the display to block to force a line break afterwards. This works in Safari too, and would work fine in Opera if it didn't generate an empty placeholder for the image when images are disabled (hence the need to absolutely position a box with it set as the background over the text in the browser).
This rule removes any Opera-specific styling from what Mozilla and Safari renders.
This is where your styling comes into play. Declare the height of the image, and optionally style the header text that will appear if images are disabled.
This is multiple-selector rule is for Opera and IE. Declare the image as the background, and declare its size.
The last rule of them all, this inserts the image in Mozilla and Safari. Very intuitive, very simple.
With decreased markup comes increased styling. The complex CSS requires declaring the image source twice (instead of the ideal once). Ideally, we could use the following CSS3 rule:
#fancy { content: url(fancyheader.gif), contents }
And accomplish the effect in all browsers. However, Opera is currently the only browser which supports content-replacement in the element, but it doesn't support the contents keyword. That makes this something to hope for in the future, but not something we can work with now.
Please feel free to email me with any suggestions, comments, criticism, questions, etc. you might have.