How to Detect Browser Support for CSS3 Properties

Share this article

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!

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

browserCSSCSS3detectionfeature
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week