CSS3 introduced a new text-shadow
property that allows for, well, shadows to be placed on text. As of February 2011, text-shadow
is only supported in 48% of browsers in use. Internet Explorer 8 and below do not support text-shadow
; surprisingly Internet Explorer 9 doesn't either. Because the majority (90%) of non-IE browsers already supports text-shadow
s natively, it's most interesting to try and polyfill IE. I've created a Textshadow jQuery plug-in for just this purpose. Read below for an overly thorough explanation of hacking IE filters to get a convincing text shadow.
Goals
The goal is to create a text-shadow
polyfill for IE 9 and below and other browsers that lack support. It must look similar to the implementation in supported browsers and follow the W3C specification as closely as possible.
- Shouldn't affect content layout
- Should flow with content
- Should follow W3C specification
Text Shadow
Below is the example of the goal: the native text-shadow as rendered in Chrome 9. The shadow is red to add the greatest contrast between the black text and the white background.
|
For these examples the simple HTML above is used. The .box
is used for forcing the text to wrap and the funky b
and i
elements in the word "shadow" are for testing how shadows are applied to child content.
|
None of the browsers that support text-shadow
require a vendor prefix, making text-shadow
extremely easy to implement. As noted above, this will not work in IE including version 9.
Using IE Filters
While IE doesn't directly support the CSS3 text-shadow
property, it does support proprietary filters. Of course these filters have drawbacks; namely, IE 8 and below apply them very slowly and they have poorly documented features. For instance, the IE filters will apply to text unless the element has a background applied; the filters will also apply to borders if they are present. Another issue that will become clear later is that IE renders the text with alpha-transparent artifacts that make the text harder to read.
There are a few IE filters that seem promising. Most often the shadow filter is proposed as an alternative to native text-shadow
although the most compelling articles use a combination of blur and glow to achieve a text-shadow
effect (another great example only uses blur).
Shadow
The example above is the proprietary shadow filter in Internet Explorer. This filter takes the original color and fades it to transparent. The filter takes three arguments for color
, direction
and strength
. Direction is taken as an angle. To approximate a 2px X and 2px Y offset, a direction of 135 degrees (135 degrees is 90 degrees plus 45 degrees (duh?)) and strength of 2 is chosen.
color
- hex color without leading hash (#) character.direction
- angle in degrees. Zero degrees is straight up.strength
- length in pixels without any units.
|
Changing the text to white will reveal some of the artifacting issues mentioned above. There are partially black pixels dotted around the perimeter of the text. This is the result of IE using a black matte behind the alpha transparent pixels in the text. I haven't fully experimented with how to remove this artifacting but it will likely involve turning off font smoothing in IE if it's possible.
Results
- The shadow gradient produced is not a blur.
- The gradient treatment causes the shadow to appear pink instead of red.
- Shadow does not follow W3C Spec. With a small
strength
(like 2px) the shadow looks similar to the nativetext-shadow
. With a higherstrength
it becomes apparent that the shadow strength is not an offset. - The artifacting is distracting and makes the text harder to read.
DropShadow
Dropshadow will make a copy of the text and offset it behind the original text. This is very similar to the W3C spec for text-shadow
with the exception of blur being unsupported. As shown above, the shadow is not anti-aliased.
color
- hex color without leading hash (#) character.offX
- length in pixels without units for the offset along the X axis.offY
- length in pixels without units for the offset along the Y axis.
|
With white text it is evident that dropshadow suffers from the same black pixel artifacting as shadow.
Results
- Blur isn't supported in dropshadow.
- Dropshadow otherwise follows the W3C spec for dropshadows by creating a copy of the text.
- The artifacting is distracting and makes the text harder to read.
Glow
Glow is yet another filter that could be used to replace text-shadow
; however, it's obvious that glow isn't very much like text-shadow
at all. But it could be used to make text stand out from a background, which is essentially what text-shadow
is used for. The color gradient effect is similar to the shadow filter but for glow, the color is omni-directional.
It should be noted that the glow affects the positioning of the text. A 2px glow pushes the text down 2px and to the right 2px. A 20px glow pronounces the issue, predictably pushing the text 20px down and to the right. The other filters, shadow and dropshadow, exhibit the same behavior when the shadow is projected up or to the left. This can cause unexpected cross-browser display issues.
color
- hex color without leading hash (#) character.strength
- length in pixels without any units.
|
Again, white text reveals the same distracting artifacting as shadow and dropshadow.
Results
- Glow isn't at all like the W3C specification for
text-shadow
. - The gradient treatment causes the glow to appear pink instead of red.
- Unexpected text offset will cause cross-browser display issues.
- The artifacting is distracting and makes the text harder to read.
Blur
Blur is perhaps the most frustrating filter because it is both exactly what is needed and not at all appropriate. The blur effect is very similar to the blur other browsers use for text-shadow
. But of course, the text is rendered unreadable. It should also be noted that the blur offsets the text the same way glow does. For this example the text is shifted 2 pixels down and to the right by the blur. The larger the pixelRadius
is, the larger the unintended offset will be, similar to the glow filter.
pixelRadius
- length in pixels without any units. This is equivalent to the blur-radius argument fortext-shadow
.makeShadow
- turns the text black no matter what color it was originally. Never use this.shadowOpacity
- only applies when makeShadow is set to true. Sets opacity and expects a value between 0 and 1. Never use this.
|
Results
- Blur creates the desired blur effect.
- Blur obscures the original text which isn't exactly desirable.
- Blur doesn't allow for color to be set directly, although it does have a useless setting that will turn the text black.
Making Blur Work
Using a few span
tags and duplicating the text allows for the blurred text to be placed behind the original text. By wrapping the original text in a span
, duplicating the original text and adding it as a second span
, and wrapping both in a third span
, it becomes simple to position the blurred text as a shadow. As is shown above, using absolute positioning for the shadow can have undesired results. The absolute positioned span
is collapsed to be as wide as the longest word. However, it is clear that the word "Some" has an appropriate text shadow. It is possible to support shadow color simply by setting the color
property on the blurred text.
|
The .shadow
wrapper is given a position: relative
to allow for proper positioning of the .shadow-copy
. The .shadow-copy
is given absolute positioning and a z-index
of 1. The .shadow-orig
is given position: relative
and a z-index
of 2 in order to place it above the .shadow-copy
. The top
and left
properties can be applied to .shadow-copy
to approximate the X and Y text-shadow
offsets. In this case, top
and left
are set to zero because the blur filter already shifts the shadow by 2px as described above.
Fixing the Wrapping Issues
Instead of wrapping a long string of text in the span
elements, it is more reliable to wrap individual words. This ensures that the .shadow-copy
is always an appropriate width without having to set a hard width
property in the CSS. Setting a hard width on the span
would cause issues if the text-size
were to change or the containing block changed size dynamically.
|
The above is an example where the simple threesome of span
elements has been applied to every individual word. Note that in the word "shadow", a span
is nested inside each b
and i
element as well.
Turning it into a jQuery Plugin
I have created a simple jQuery plugin called jQuery Textshadow that automatically wraps each word in the span
s as shown above and applies the appropriate blur filter and positioning for the shadow. It even supports RGBA and HSLA by converting the values to a hex color and applying the alpha filter to the shadow. The plugin requires that a small CSS file also be included on the page in order to set up some base styles for the spans.
|
Interesting Notes
Currently, the plug-in assumes it is being applied in IE only and doesn't do any feature detection. It should be applied using a tool like Modernizr or IE conditional comments. It's not particularly speedy, especially if there are dozens of tags that need the shadow applied. The slowest part is actually wrapping the text nodes with the replacement HTML. It's acceptably fast in Internet Explorer 9 and noticeably slow in Internet Explorer 8.
Curiously, Internet Explorer 9 also applies a cleaner shadow that is closer to text shadow in other browsers. The blur in IE 8 is noticeably muddier than the browsers that support text-shadow
. See the images below for comparison.