(faux) text-shadow in IE9


IE9, while a great improvement on IE8, still makes us tear our hair out. text-shadow, a property not requiring vendor prefixes in any of the other major browsers, is entirely unsupported in IE9.

The following technique will show how we can overcome some of the limitations of Internet Explorer 9 with a helping hand from our very good friend, the pseudo-element.

Creating a text-shadow in IE9

text-shadowed heading

faux text-shadowed heading (ie9 only)


Viewing the above example in IE9 will show that a text-shadow is applied only to the second heading, while in any other modern browser, only the first, and true text-shadow'd heading has a drop-shadow.

The secret is both in the html, and the css:

<!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if IE 9]>    <html class="no-js ie9 oldie" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->

<!-- head, body etc. opened -->

<h1 id="this-text-shadowed-heading" class="shadow" data-content="This text-shadowed heading">This text-shadowed heading</h1>

<!-- body, html closed -->
.shadow {
  text-shadow: 3px 3px 0 hsla(0,0%,0%,.1);

.ie9 .shadow, .ie9 .ie9-only-shadow { position: relative;}

.ie9 .shadow:before, .ie9 .ie9-only-shadow:before {
  position: absolute;
  content: attr(data-content);
  color: hsla(0,0%,0%,.1);
  width: 100%;    top: 3px;
  left: 3px;
  z-index: -1;

You will notice all the conditionals on the html tag. These help in a range of ways not fit for the scope of this post to explain. I urge you to take a look at the HTML5 boilerplate - it can drastically improve the way you develop websites.

Furthermore, you will notice that there is a data-content attribute in the h1. The data-* attribute (as of HTML5) allows you to create custom attributes in your elements.

I have simply copied and pasted the content of the heading, and placed it into the data-content attribute. This could be automated using javascript, or added on the fly using PHP.

UPDATE 2011/11/16

Stan was kind enough to edit my jsfiddle to have the data-content attribute added dynamically to the headings. Grab the snippet!


A blur can not be applied to the shadow in IE9, not even with Microsoft's proprietary filters. The Blur Filter doesn't seem to work when applied to pseudo-elements, and the filter seems poorly implemented anyhow.

Using this method requires duplication of content - which is never a good thing. It should be reserved for headings, and short pieces of text.

Copying and pasting of the contents of elements is tedious, and poor form. This technique can be automated using javascript, and a marker class on the elements you want IE9 to have a text shadow on. Conditionally adding the data-content attribute for IE9 visitors ONLY would be a good idea too.

This technique fails in IE8. I have struggled, to no avail, to get the pseudo-element to sit underneath its element using z-index. Please leave a comment if you manage to discover a fix for IE8.

Other methods

CEOBot from Switchup Studios enlightened me to another method whereby the pseudo-element is made to display as a block level element, and then positioned using negative margin. This technique can be useful, but has its drawbacks in that it requires a unique negative margin calculated for each and every instance in which it is applied.

Grady Kuhnline has a thorough article on CSS text shadows in Internet Explorer using IE's proprietary filters. A must read if you want to understand what exactly can be done with Internet Explorer's own tools.

UPDATE: CEOBot's negative margin method will work in IE8 without issue - thanks for the update :)

Let me know if you come across techniques that are more effective than what I have shown here!

6 Responses

rss feed
  1. Hey :)

    The fix I told you about does work in IE8 perfectly, I didn’t see a mention for that in the article :D

    Also to fix the ‘static-ness’ of either method, you could use WordPress built in PHP functions (or make your own!) to include the relevant text in the ‘content’ attribute. For instance, if you were using the pseudo method on your blogs title:

    content: “”;

    Would do it on the fly! So you wouldn’t need to jump into the code everytime a title change was needed :D You could further do this for article titles and anything else that can be referenced in WordPress :D

    Also, just quickly, could you attribute it to ‘CEOBot’ instead of ‘Pinwheel’. Pinwheel does the design stuff, I (CEOBot) do the coding stuff :D

    Thank you for the mention! We appreciate it a lot :)

  2. Woops it seemed to get rid of the PHP code I use. Here it is again, I’ll try it a few different ways to get it to work :D

    content: "<?php echo bloginfo( ‘name’ ); ?>;

    content: “[]php[] echo bloginfo( ‘name’ );[][]”;;

    • Awesome, updated!

      We actually output all our id’s on headings on the fly (hence the id in my example), too – makes referencing key sections of posts that little bit easier :)

      haha, ye, we just got our site up 2 weeks ago… got a bit of fiddling with comments etc. still! Thanks for the heads up, CEOBot!

    • The site is looking awesome! Keen to come back and read some more articles and whatnot soon :D

      Thanks again for the reference :)

  3. You can also add the the data-content attribute dynamically on the client side using JavaScript – in this case jQuery:

    $(‘h1.ie9′).attr(‘data-content’, $(‘h1.ie9′).text());

    Fiddle updated :)

This comment thread is closed. Got something important to say? contact us!