How to Detect Browser Support for CSS3 Properties

Feature detection used to be so much easier.

Browser sniffing was a reasonable solution in the early days of the web. Developers would check the user agent and run JavaScript code which targeted an application. It was painful but often necessary because browser technologies varied significantly.

The rise of web standards reduced the need for code forking. Internet Explorer’s event model and XMLHttpRequest implementation was inconsistent with W3C standards but a little object detection overcame those hurdles. A large proportion of our code would work everywhere.

We now have HTML5 and CSS3. No browser supports every feature so it’s often necessary to include shims or use detection techniques to ensure cross-browser compatibility. Consider this embossed text example:


body
{
	font-family: sans-serif;
	background-color: #fff;
}

.emboss
{
	font-size: 2.5em;
	font-weight: bold;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.4);
}

Most modern browsers display a pleasing effect:

Embossed Text

To achieve it, we’ve set the font to the same color as the background. Unfortunately, this makes the text invisible in browsers which don’t support text-shadow. This includes Internet Explorer 9.0 and older editions of all browsers.

Modernizr to the Rescue!

Modernizr is an amazing library which detects CSS properties, transformations, HTML5 elements, canvas, SVG, geolocation, local storage, touch events, and more. The minimized gzipped edition is only 3.7kb and you can reduce it further by downloading a customized build.

Modernizr provides a JavaScript API and appends class names such as textshadow, opacity, cssgradients, svg, etc. to the html element. We can therefore rewrite our embossed text CSS accordingly:


.emboss
{
	font-size: 2.5em;
	font-weight: bold;
	color: #333;
}

.textshadow .emboss
{
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.4);
}

If you need to detect a varied range of features, Modernizr is one of the best solutions available.

Rolling Your Own Detection Code

A third-party library may be overkill if you only want to detect a few CSS features. Modernizr and similar solutions work by creating an element (not appended to the DOM) and testing the value of a CSS property. An unsupported property normally returns ‘undefined’.

text-shadow is one of the easier properties to detect — the following code appends a “textshadow” class to the html element if it’s supported:


// detect CSS text-shadow support in JavaScript
if (document.createElement("detect").style.textShadow === "") {
	document.getElementsByTagName("html")[0].className += " textshadow";
}

Properties with vendor prefixes are a little trickier. For example, boxShadow may not be directly supported but one of these properties may be: WebkitBoxShadow, MozBoxShadow, OBoxShadow, msBoxShadow, KhtmlBoxShadow. It’s therefore necessary to loop through the options, e.g.


// detect CSS box-shadow support in JavaScript
var d = document.createElement("detect"),
	CSSprefix = "Webkit,Moz,O,ms,Khtml".split(","),
	All = ("boxShadow " + CSSprefix.join("BoxShadow,") + "BoxShadow").split(",");
	
for (var n = 0, np = All.length; n < np; n++) {
	if (d.style[All[n]] === "") {
		document.getElementsByTagName("html")[0].className += " boxshadow";
		break;
	}
}

This is a little long-winded and you wouldn’t want to write the similar code for every property. Therefore, we’ll wrap the functionality in a module which detects CSS text-shadow, text-stroke, box-shadow, border-radius, border-image, and opacity support:


// CSS support detection
var Detect = (function() {

	var 
		props = "textShadow,textStroke,boxShadow,borderRadius,borderImage,opacity".split(","),
		CSSprefix = "Webkit,Moz,O,ms,Khtml".split(","),
		d = document.createElement("detect"),
		test = [],
		p, pty;
	
	// test prefixed code
	function TestPrefixes(prop) {
		var
			Uprop = prop.charAt(0).toUpperCase() + prop.substr(1),
			All = (prop + ' ' + CSSprefix.join(Uprop + ' ') + Uprop).split(' ');

		for (var n = 0, np = All.length; n < np; n++) {
			if (d.style[All[n]] === "") return true;
		}
			
        return false;
	}

	for (p in props) {
		pty = props[p];
		test[pty] = TestPrefixes(pty);
	}

	return test;

}());

The values of Detect.textShadow, Detect.textStroke, Detect.boxShadow, Detect.borderRadius, Detect.borderImage, and Detect.opacity return true if they’re supported. If required, we can append associated class names to the html element:


// append to HTML node
var html = document.getElementsByTagName("html")[0];
for (t in Detect) {
	if (Detect[t]) html.className += " " + t.toLowerCase();
}

Or display a list of supported properties:


for (t in Detect) {
	document.write(
		"CSS " + t + " support? " + 
		(Detect[t] ? "YES" : "NO") +
		"<br>"
	);
}

The demonstration page shows this code in action. You can use it as the basis of your own detection library — or perhaps it’s easier to include Modernizr and be done with it!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Mike

    It’s interesting how a few years ago there was a general consensus that sites had to work with JavaScript disabled. Now that HTML5 and CSS3 are providing the fun stuff, that seems to be changing. JS is becoming the old standard that everyone is expected to be running, and H5/C3 are the toys we progressively enhance towards.

    Have the users on IE6/7/8 that we used to fallback for all either enabled JS or upgraded their browsers?

    Obviously if they miss out on rounded corners due to a lack of JS it’s no big deal, but the white-on-white text example is a little more critical.

    • Stormrider

      The whole point of the library though is to make sure that users without support don’t get the white on white text. Look at the code – if JS is disabled, the colour will be #333. Only when JS is enabled AND the library detects support for the text shadow does the colour change to #fff.

    • http://www.optimalworks.net/ Craig Buckler

      Thanks Stormrider – that’s spot-on.

      The article highlights why it can be important to detect CSS3 features. Until recently, developers rarely needed to do that because CSS offered few effects which would cause problems in browsers which didn’t support it.

      It’s simply another level of progressive enhancement:

      All browsers get the HTML content.
      Some browsers will apply CSS2.1 styles.
      Some browsers will apply CSS3 effects if they’re supported and JavaScript is enabled.

      Open the demonstration page in Firefox, Chrome or Opera and compare it to IE. “Embossed Text” remains readable no matter what browser/settings are used.

  • deathshadow

    Advocating… browser… sniffing…

    WOW… just… WOW… I lack the words in polite company to say what I’m thinking.

    • Stormrider

      Guess you need to read the article again then cause there is no advocating of browser sniffing at all!

    • Immysl

      It’s not browser sniffing but feature detection. The Modernizr developers are against browser sniffing.

  • 3Easy

    deathshadow, you are incorrect. Feature detection is what is being advocated, using the Modernizr library. Quite distinct. Please read again.

  • IT Mitică

    I actually have quite the respect for libraries like Modernizr and jQuery. They are the almost-standard droplets in a chaotic world. Real life savers if you are willing to appreciate their value. Plus, the work is done for free for you to use. A big big plus!

    There are some pros and cons.

    1. A problem I see, is illustrated in the fallowing scenario:

    - I use a modern browser, supporting HTML5 and CSS3, as much as it does
    - I surf using NoScript

    This means I’m not going to enjoy HTML5 and CSS3 experience, even though, theoretically, I should.

    2. On the other hand, if I don’t use something like Modernizr, then, lesser browsers may be unable to provide a graceful degradation, like in the case of multiple backgrounds, leaving me with the sole option of supporting only better browsers.

    3. In the end, forgetting about scenarios, however useful, there is a certain degree of added complexity when using something like Modernizr.

    That’s why I prefer to tackle the problem somewhat different, and choose not to use JS libraries to alter my markup.

    • Immysl

      I do the same thing. Was a wondering whether there are others who fall in my line of thinking and here I found you. :)

    • http://www.optimalworks.net/ Craig Buckler

      You only need to use Modernizr or a similar solution if a CSS3 technique (or combination of effects) causes a problem in a lesser browser. I’ll show you another example in a few days…

      If you were just adding rounded corners, there’s no need to use a rounded-corner detection script because that effect degrades gracefully.

    • Patrick Samphire

      I’ll be honest, I think that if you choose to surf with JavaScript turned off, then you are hobbling yourself by choice. It’s almost becoming akin to surfing with CSS turned off.

      A few years ago I think I would have agreed with you. But these days JavaScript is becoming more and more central to the web, and choosing to turn it off is becoming less and less justifiable. Of course you *can*, if you want, but it’s kind of like choosing to surf with IE5.5: don’t expect developers to support you as a visitor.

      That being said, where possible I make sites that you can use with JavaScript and/or CSS turned off; it’s just that I’m no longer willing to let the lack of these technologies in a small number of visitors hold back something important that I want to do.

      • IT Mitică

        No matter the reasons, there are those who choose to surf with JS turned off.

        As an <aside> :), NoScript puts me in control of what I want to run on my computer and what I don’t.

        And it’s a pretty good way this way, since not all JS code out there is intended to make my web surfing experience better, we all know this.

        Never mind sites that have a heavy, unreliable JS code that’s crashing my browser.

        With this </aside> ended :), unless we speak of web apps, any web site should be functional without any of CSS or JS code.

        These two are for making a better user experience, right, but not to be the deal breaker or maker for any of your visitors.

        That’s why I prefer not to rely on the use of JS to make CSS3 happen on lesser browsers as well. It can lead to me losing CSS3 for browser that can, in fact, exploit this new world.

      • http://www.optimalworks.net/ Craig Buckler

        @IT Mitică
        Note that this isn’t about using JS to make CSS3 on lesser browsers. It’s about ensuring CSS3 effects do not get partially applied on lesser browsers which leads to visibility or usability issues.

      • IT Mitică

        Agreed. It’s kind of relative though.

        The methods used rely on JS.

        Expecting the JS detection of CSS3 feats to be 100% reliable is not realistic for me.

        I have a choice to make.

        1. “Ensuring CSS3 effects do not get partially applied on lesser browsers which leads to visibility or usability issues.”

        If I develop my HTML and CSS code with lesser browsers in mind, expecting JS corrections, those may not happen. Lesser browsers will not be improved, and better browsers will never get the code to showcase newer capabilities.

        2. I don’t start from the bottom, with (hopefully) HTML4 & CSS 2.1, and make my way up, with the help of JS, but I start from the top, with HTML5 & CSS3.

        If I develop my HTML and CSS code with better browsers in mind, I have the choice of providing a graceful degradation using CSS alone, instead of playing both JS and CSS.

        Which is better? I don’t know. I know I’m doing the later right now, looking at the my current target browsers line-up: IE7+, FF 3.6+, Op 11+, Saf 5+, Ch 10+.

        Other choices in browsers line-up may bring other choices, including the use of JS code to help CSS.

      • http://www.optimalworks.net/ Craig Buckler

        It depends what you want to achieve. The embossed effect above is great but it’s hardly essential. It may be best to avoid it if, say, you’re running a busy online shop.

        But, if you want to use a combination of cutting-edge techniques which look great and save time, CSS3 feature checking may be a necessary evil. JavaScript detection is reliable; those who disable it won’t see the effect, but they can still use the site.

  • Norguard

    @IT,

    A quick example of why this feature is useful:

    I am attempting to create a logo / watermark using one single H2 element and the text inside of an included anchor.

    With CSS3′s border-radius, box-shadow (and box-shadow: inset) and multiple text-shadows it becomes very easy to create something that would have required photoshop not more than a few years ago.

    I save markup, I save time, I save weight and most-importantly, I save a GET request at the top of the page.

    Thing is: everything is shades of blue. While the border and the text are a different hue than the background, the text-shadows are still needed for clarity: otherwise there is no contrast, and the text / border are marginally darker than the background, leading the text to look “out of phase” (audio term, but it works perfectly well, here) with the light-text / dark-background header.

    This means that even up to IE9, there is no way for me to *start* with the CSS3 version of this h2 element, and “undo” it for IE.

    CSS4 would DEFINITELY benefit from JavaScript-like Closures, that are only ever calculated once (unless overwritten), and then apply across the board.

    h2 {
    return (text-shadow) ? { text-shadow : …..; } : { background-image : url(…); }
    }

    Frustratingly, the only way that would ever work is if the old browsers were the ones with support for the closures…

    so back to the real-world problem:

    My options are:
    a) scrap the idea and come up with something else
    b) start with CSS basics, and use JavaScript to enable CSS3, accepting that those without JS are going to get a sub-optimal viewing experience (which is fine, because it’s a media site – if you don’t have JavaScript enabled, you’re going to have a sub-optimal experience, period)
    c) start with CSS3, and use JavaScript to replace the text inside of the h2 with a screencap of the h2 element, if CSS3 text-shadow fails…
    (should lead to the same sort of perception of texture streaming in current console games)

    I don’t really have a solution for my problem, which isn’t based either in JavaScript, or in going back to the stone-ages.

    So which should it be : IE9 (/IE8/IE7) users without JavaScript see a CSS2 version of the logo, or they see a soupy-mess, until the JavaScript kicks in at page-load, and replaces the blue dot with an image?