3 posts on WebKit Bugs

Exploring CSS3 text-shadow

5 min read 0 comments Report broken page

I consider CSS3’s text-shadow one of the most exciting CSS3* properties, which offers us a lot more effects than it’s name suggests. Of course, it can be used for creating drop shadows for text, and it carries out that task very well, but it’s inherent flexibility allows it to be also used for glow effects, outlines, bevels, extruded text, inset text, fuzzy text and many others (until browser bugs and backwards compatibility come into play… :(). This post is about various findings of mine (and others’, where a source is provided) regarding this property, including browser bugs and inconsistencies, effects that can be achieved with it, compatibility woes etc.

Browser support

  • Opera 9.5+
  • Firefox 3.5+
  • Safari 1.0+
  • Google Chrome

text-shadow syntax

The syntax is fairly simple:

text-shadow: <offset-x> <offset-y> <blur-radius> <color>;

There are some variations (the color could be first instead of last, the blur radius can be omitted if it’s equal to zero and the color may be omitted if it’s the same as the text color) and you may include multiple comma delimited shadows.

You may read more about the syntax in the official specification.

How it works

It helps if you imagine the algorithm for drawing the text shadow as follows:

  1. Create a (most of the times differently colored) clone of the text and place it behind the text.
  2. Move it according to the X and Y offsets (positive values move it to the right and bottom respectively)
  3. If a blur radius is specified and it’s > 0, blur it accordingly (the specification doesn’t mention the blurring algorithm to be used, so each browser vendor may choose any blurring algorithm they prefer, and judging by my experiments, it seems they took advantage of this freedom). In all cases however, the bounding box of the blurred text can extend no further than the bounding box of the original text plus (+) the specified blur radius on each side.
  4. Repeat for the rest of the shadows, if more than 1 are specified. The order in which shadows are drawn seems to be a subject of debate, judging by the wording of the specification and the various existing implementations.

The experiments

You will find the experiments I performed here. I tried to come up with (or find) interesting uses of the property. I also tried to make some of them “pretty”, so they could be useful to others, but given the fact that these were primarily created for testing purposes, this wasn’t achievable for all of them. Next to each experiment is the CSS used to produce the effect (directly fetched from the <style> tag via JavaScript). You’d better not view it with IE until you read below or you might have some freaky nightmares tonight :P

Screenshots from various browsers: (mouse over the thumbnails to see which browser was used for each one)

[gallery link=“file”]

Browser bugs and inconsistencies

Apparently, some browser bugs were exposed in these experiments:

  • Opera 10 and Safari don’t display the shadow when the text color is transparent (demonstrated in Experiment #5). Opera 9.6 doesn’t seem to support transparent as a text color, so it ignores it.
  • When the text color is RGBA, Safari applies transparency to the shadow, equal to the Alpha component (demonstrated in Experiment #8).
  • Opera paints the shadows in the order they were specified, whereas all others use the reverse. According to the current version of the specification, Opera is the only correct one, but I doubt that web designers will give her credit for it :p (Experiment #8)
  • Google Chrome uses a crappy blurring algorithm (Experiments #5 and #7)
  • Safari and Chrome don’t default to the text color when no color is specified in text-shadow, but to transparent. (Experiment #2)
  • Opera is seriously messed up when it comes to transparent shadows, as demonstrated by Experiment #9. I can’t even describe the bug (try messing with the text-shadow value a bit and you’ll see why…). Luckily, I can’t think of a single case where a transparent text-shadow would be useful :P
  • You can see a bit of the shadow in Google Chrome even if the offsets and blur radius are all 0 (Experiment #9). I’m not sure if this is a bug, but it’s inconsistent with the other implementations.
  • Even if you ignore the bugs above, there are slight rendering variations when multiple blurred shadows are involved (or they are more apparent in those cases), as demonstrated by experiments #2, #6 and #7.

Firefox’s implementation seems to be the clear winner here…

A note about the above observations: When no version number is mentioned, 3.5 is implied for Firefox, 10 for Opera and 4 for Safari and Chrome.

Alternatives to text-shadow

IE Filters

As you might have noticed, I have managed to completely avoid mentioning Internet Explorer up to this point. It’s no surprise that our dearest browser doesn’t support the text-shadow property. However, it does support some filters (DropShadow and Shadow) that could be used to provide a very small subset of the different kinds of text shadows, although they severely mess up the font anti-aliasing (just like all IE filters). Also, if the parent or siblings of the text node in question have backgrounds or borders an extra element is needed to enclose the text node (you’ll see in the experiments why…). For these reasons,  I highly doubt whether they are worth it and I don’t use them personally. However, if you are interested you can see a brief demonstration of these two filters in Experiments #3 (DropShadow) and #6 (Shadow, actually 4 of them).

The :before pseudo-element

This could be used instead of the text-shadow, when the blur radius is 0, since browser support for the :before pseudo-element is better than browser support for text-shadow (even IE8 supports that, yay). Here is a thorough (although slightly outdated) tutorial on this technique. However,this workaround severely hurts separation of presentation and content/structure, since the content has to be duplicated in the CSS. Repeating something greatly increases the chance that the two copies become inconsistent, since people tend to be forgetful. Also, you have to know in advance the exact height of the text (in lines), another maintenance headache. For these reasons, I don’t use this workaround either.

In my humble opinion, the text shadow is usually just icing on the cake and not something crucial to the design, so it doesn’t hurt if it won’t show in some old and/or crappy browsers. It degrades gracefully in most cases (ok, you’ll have to wait a few years before using it in ways that don’t) so it doesn’t hurt usability/accessibility either. It’s just one of the little treats I like to offer to visitors that were smart enough to use a decent browser. :-)

Epilogue

text-shadow is a very flexible property, with probably the best browser and editor – even Dreamweaver acknowledges it’s existence! – support among all notable CSS3* properties. It also degrades gracefully most of the times (the experiments above shouldn’t be considered “most of the times”! :P ) and this is why it’s probably also the most widely used CSS3* property.

I think it could be improved even more by allowing for the inset keyword (just like inset box-shadows – sadly only Firefox 3.5 supports those at the time) and a fourth parameter could be used to enlarge or shrink the shadow (currently the only way to enlarge it is by blurring it, which isn’t always desirable) although it would complicate the shorthand (the blur radius would probably become required – so that the browser can tell them apart). However, a separate property could be used to solve that (text-shadow-size?). I guess we could combine the :before technique, with transparent text color (in the :before pseudo-element) and a text-shadow for that to imitate such an effect (I can elaborate if this seems obscure) although I haven’t actually tried it (however, even if it works, it’s too much of a hassle).

Anyway, I guess it’s too late for such suggestions, so let’s focus on what we actually will get (?) which is more than sufficient :-)

______________________________________________________________

*Actually, it was originally proposed for CSS 2.1, but it was dropped due to lack of implementations (basically only Webkit supported it)

Articles, Browser Bugs, Browsers, Chrome Bugs, CSS, CSS Properties, Experiments, Opera Bugs, Safari Bugs, Text Shadow, WebKit Bugs
Edit post on GitHub

On native, single-input, multiple file uploads

2 min read 0 comments Report broken page

If you are following the current news on web development, you probably heard that the new Safari 4 has a great feature: It natively allows the user to select multiple files via a single input control, if you specify a value for the attribute multiple:

<input type="file" multiple>

or, in XHTML:

<input type="file" multiple="multiple" />

You might not know that Opera supported multiple file uploads for a while now, based on the earlier Web Forms 2.0 standard in a slightly different (and more flexible) format:

<input type="file" min="1" max="9999″ />

Can we use those currently?

Sure we can, but we should provide fallbacks for the other browsers. Using these features will put pressure on the other browser vendors to implement them as well and generally, native is always better.

How can we find out whether the browser supports them?

Opera

Opera supports accessing those min and max properties as properties of the element. So, it’s quite trivial to check whether Opera-style multiple inputs are supported:

var supportsMin = (function(){
	var fi = document.createElement('input');
	fi.type = 'file';
	return fi.min === '' && fi.max === '';
})();

Safari 4

In Safari 4 the check would be equally simple, if it supported accessing the multiple attribute as a property. Then we could easily check whether it’s boolean and conclude that Safari-style multiple inputs are supported:

var supportsMultiple = (function(){
	var fi = document.createElement('input');
	fi.type = 'file';
	// The second check is probably redundant but what if in the future an implementor
	// decides to make the file inputs to handle multiple selections by default?
	// Yeah, it's not likely, but it's not entirely impossible.
	return fi.multiple === false || fi.multiple === true;
})();

However, that’s currently not the case. The good news are that I reported this as a bug today, and the Webkit team fixed it, so it will be possible in the next Webkit nightly!

Combining the two

You can easily combine these two together with the workaround you prefer:

// Create a file input that allows multiple file selection
var fi = document.createElement('input');
fi.type = 'file';

if(fi.multiple === false || fi.multiple === true) { fi.multiple = true; } else if(fi.min === ‘’ && fi.max === ‘’) { fi.min = 1; fi.max = 9999; } else { // Our preferred workaround here }

What about Mozilla?

Ok, we all know that IE will probably take years to implement similar functionality. But usually, the Mozilla team implements new and exciting stuff quite fast.

As it turns out, there is a relevant ticket sitting in their Bugzilla for a while now. If you want them to implement it, vote for it so that it’s priority increases.

If they do implement it in the way suggested, the code posted above will work for that too, without any changes - The advantages of feature detection baby! ;)

Articles, News, Browsers, Feature Detection, HTML, JS, WebKit, WebKit Bugs
Edit post on GitHub

Find the vendor prefix of the current browser

3 min read 0 comments Report broken page

As you probably know already, when browsers implement an experimental or proprietary CSS property, they prefix it with their “vendor prefix”, so that 1) it doesn’t collide with other properties and 2) you can choose whether to use it or not in that particular browser, since it’s support might be wrong or incomplete.

When writing CSS you probably just include all properties and rest in peace, since browsers ignore properties they don’t know. However, when changing a style via javascript it’s quite a waste to do that.

Instead of iterating over all possible vendor prefixes every time to test if a prefixed version of a specific property is supported, we can create a function that returns the current browser’s prefix and caches the result, so that no redundant iterations are performed afterwards. How can we create such a function though?

Things to consider

  1. The way CSS properties are converted their JS counterparts: Every character after a dash is capitalized, and all others are lowercase. The only exception is the new -ms- prefixed properties: Microsoft did it again and made their JS counterparts start with a lowercase m!
  2. Vendor prefixes always start with a dash and end with a dash
  3. Normal CSS properties never start with a dash

Algorithm

  1. Iterate over all supported properties and find one that starts with a known prefix.
  2. Return the prefix.
  3. If no property that starts with a known prefix was found, return the empty string.

JavaScript code

function getVendorPrefix()
{
	var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

var someScript = document.getElementsByTagName(‘script’)[0];

for(var prop in someScript.style) { if(regex.test(prop)) { // test is faster than match, so it’s better to perform // that on the lot and match only when necessary return prop.match(regex)[0]; }

}

// Nothing found so far? return ‘’; }

Caution: Don’t try to use someScript.style.hasOwnProperty(prop). It’s missing on purpose, since if these properties aren’t set on the particular element, hasOwnProperty will return false and the property will not be checked.

Browser bugs

In a perfect world we would be done by now. However, if you try running it in Webkit based browsers, you will notice that the empty string is returned. This is because for some reason, Webkit does not enumerate over empty CSS properties. To solve this, we’d have to check for the support of a property that exists in all webkit-based browsers. This property should be one of the oldest -webkit-something properties that were implemented in the browser, so that our function returns correct results for as old browser versions as possible. -webkit-opacity seems like a good candidate but I’d appreciate any better or more well-documented picks. We’d also have to test -khtml-opacity as it seems that Safari had the -khtml- prefix before the -webkit- prefix. So the updated code would be:

function getVendorPrefix()
{
	var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

var someScript = document.getElementsByTagName(‘script’)[0];

for(var prop in someScript.style) { if(regex.test(prop)) { // test is faster than match, so it’s better to perform // that on the lot and match only when necessary return prop.match(regex)[0]; }

}

// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object. // However (prop in style) returns the correct value, so we’ll have to test for // the precence of a specific property if(‘WebkitOpacity’ in someScript.style) return ‘Webkit’; if(‘KhtmlOpacity’ in someScript.style) return ‘Khtml’;

return ‘’; }

By the way, if Webkit ever fixes that bug, the result will be returned straight from the loop, since we have added the Webkit prefix in the regexp as well.

Performance improvements

There is no need for all this code to run every time the function is called. The vendor prefix does not change, especially during the session :P Consequently, we can cache the result after the first time, and return the cached value afterwards:

function getVendorPrefix()
{
	if('result' in arguments.callee) return arguments.callee.result;

var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

var someScript = document.getElementsByTagName(‘script’)[0];

for(var prop in someScript.style) { if(regex.test(prop)) { // test is faster than match, so it’s better to perform // that on the lot and match only when necessary return arguments.callee.result = prop.match(regex)[0]; }

}

// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object. // However (prop in style) returns the correct value, so we’ll have to test for // the precence of a specific property if(‘WebkitOpacity’ in someScript.style) return arguments.callee.result = ‘Webkit’; if(‘KhtmlOpacity’ in someScript.style) return arguments.callee.result = ‘Khtml’;

return arguments.callee.result = ‘’; }

Afterthoughts

  • Please don’t use this as a browser detection function! Apart from the fact that browser detects are a bad way to code 99.9% of the time, it’s also unreliable for IE, since Microsoft added a vendor prefix in IE8 only. Before that it followed the classic attitude “We have a large market share so standards and conventions don’t apply to us”.
  • There are some browsers that support multiple prefixes. If that is crucial for you, you may want to return an array with all prefixes instead of a string. It shouldn’t be difficult to alter the code above to do that. I’ll only inform you that from my tests, Opera also has Apple, Xn and Wap prefixes and Safari and Chrome also have Khtml.
  • I wish there was a list somewhere with ALL vendor prefixes… If you know such a page, please leave a comment.