Browser support & fallbacks for CSS

Not all browser provide the same capabilities e.g. in support of some (newer) CSS features. In case we want to use a CSS feature with limited support, it is our responsibility as developers to provide a good fallback so that users with a browser that supports the feature can actually enjoy it, while the users which browser doesn’t support a feature will still be able to access our content in a similar way.

The most excellent site i am aware of is caniuse.com which provides you detailed information about browser support of a feature. Sometimes you might find that a certain feature is supported, but slightly different across browsers. For example it might need a vendor prefix, or slightly different syntax. You can almost use different syntaxes alongside and let the cascade take care of which one wins. For this reason, always place the standard version last. For example to apply box-sizing as a border-box value you might need to introduce a vendor prefix to support e.g. Android 2.3 devices.

.box {
-webkit-box-shadow: 0px 1px 4px rgba(0,0,0,0.2);
box-shadow: 0px 1px 4px rgba(0,0,0,0.2);
}

Every browser supporting the unprefixed version will use box-shadow while the android 2.3 stock browser will use the prefixed version because it has no implementation of the unprefixed version.

Most of the time it’s a good practice to provide fallbacks, so that your site doesn’t break in older browsers, even if it doesn’t look as fancy in them (Do websites need to look the same in very browser?)

Lets take as an example a linear background. If you know how the cascade works implementing a fallback is very easy (due to the fact that CSS is so fault tolerant and everything is just ignored what a browser does not understand).

If you want to use a linear gradient from yellow to red, you might to apply the average of the two gradient colors first before defining the linear-gradient. So in case the browser does not support it, it will stick to the rgb() value otherwise it will apply one of the following declarations:

.selector {
background: rgb(255,128,0);
background: -moz-linear-gradient(0deg, yellow, red);
background: -webkit-linear-gradient(0deg, yellow, red);
background: linear-gradient(90deg, yellow, red);
}

However, sometimes it is not possible to provide decent fallbacks through the cascade. As a last resort you could use tools like modernizr, which adds classes like csstransforms or no-csstransforms to the root element, so you can use them to target elements only when certain features are supported, like e.g.:

.my-headline {
color: #333;
}
.textshadow .my-headline {
color: transparent;
text-shadow: 0 0 2px #333;
}

if the feature you are trying to create a fallback for is sufficiently new, you could also use the @supports rule, which is the native modernizr. For example the CSS grid specification might me a good use case for this.

.my-element:before {
content: "does not support css grid";
}
@supports (display: grid) {
.my-element:before {
content: "supports css grid";
}
}

However, for now be wary of using @supports. By using it we limit our effort not only to browser that support the CSS grid, but also to browser that support the @supports rule. Maybe a more limited set.

Last but not least there is always the choice of using a few lines of home-baked Javascript to perform feature detection in the same fashion as a feature detection library. The main way to determine whether a property is supported is to check its existence on the element.style object of any element. An incomplete example for flexbox support might look like this:

var docRoot = document.documentElement; //html-node
if ('flexBasis' in docRoot.style) {
docRoot.classList.add('flexbox');
}
else {
docRoot.classList.add('no-flexbox');
}

If we want to test for a value, we need to assign it to the property and check if the browser retains it. Because we are modifying styles here and not just testing for their existence, it makes sense to use a dummy element:

var docRoot = document.documentElement;
var dummy = document.createElement('p');
dummy.style.backgroundImage = 'linear-gradient(blue,yellow)';
if (dummy.style.backgroundImage) {
docRoot.classList.add('lineargradients');
}
else {
docRoot.classList.add('no-lineargradients');
}

Of course if you want to test for multiple features both should be converted to a function.
Testing selectors and @rules is a more complicated, but follows the same principle: when it comes to CSS, browser drop anything they don’t understand, so we are able to check if a feature is recognized by applying it and checking if it was retained.

Keep in mind that even if a browser is able to parse a CSS feature this does not mean that the feature is correctly implemented, or even implemented at all.

Leave a Reply

Your email address will not be published. Required fields are marked *