Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

CSS With Feature Detection for Cross-Browser Compatibility

DZone 's Guide to

CSS With Feature Detection for Cross-Browser Compatibility

We take a look at several built-in components that CSS offers that allow for effective cross-browser application development.

· Web Dev Zone ·
Free Resource

The primary goal of every web developer is to build websites with modern and intuitive designs that deliver a smooth and seamless user experience, irrespective of which browser they might be using to surf the web. The Internet has witnessed a massive unprecedented boom in recent decades. As of December 2018, there are more than 4.1 billion internet users in the world and close to 1.94 billion websites on the web. This consequently implies an expansion in a number of ways websites are being accessed by audiences across the globe. This gives rise to the conundrum of cross-browser compatibility, which poses a huge challenge to developers. As the number of browsers and their versions are growing at such a rapid pace every year, the task of trying to make a website appear and perform consistently across all browsers is every developer’s nightmare. However, as tedious and time-consuming as cross-browser testing may be, it is an imperative phase of every testing cycle. While it is considered nearly impossible to have a website appear and work identically on every browser, there still are a number of ways to deliver consistent user experience and reach a wider target audience. In this article, we’ll explore what cross-browser compatibility issues are and why they occur, how CSS with feature detection is more favorable to browser detection, and tips to achieve near perfect browser compatibility when designing websites.

Why Browsers Render Content Inconsistently

browsers

Before delving deeper into cross-browser compatibility and CSS with feature detection, it is crucial to have a basic understanding of how internet browsers work and why compatibility issues occur. While browsing the web, whenever you click on a link or type a URL in your browser, you’re guiding your web browser to make a server request for loading the content that you are seeking and to display it in the browser. This is done by the use of a rendering engine that is responsible for parsing the HTML and CSS code and displaying the parsed content on the screen. As every browser has its own rendering engine, content is not displayed identically across all browsers. As a result, there might be glaring differences in the website’s layout and appearance.

Safari uses the ‘Webkit’ rendering engine, Google Chrome uses ‘Blink’(earlier webkit) along with all chromium-based browsers like Microsoft Edge and Opera, Firefox uses ‘Gecko,’ and, finally, Internet Explorer uses ‘Trident.’ Older versions of Opera used ‘Presto.’

browsers

The most dreaded enemy of every front-end developer is the inconsistent feature support among browsers. This means websites appear and work perfectly during the development phase on the browser of the developer’s choosing but might end up looking completely haphazard on other browsers during the final testing phase.

Browser Preference Bias

No matter how ardent the denial may be, every developer has a secret ‘browser preference bias.’ The browser which the developer relies upon during the website’s development acts as the standard for evaluation of the website’s performance. The majority of developers naturally have a bias towards Google Chrome which has the leading market share worldwide and is packed with the most powerful developer tools suite. This might lead developers to overlook and ignore browsers like Mozilla Firefox, Safari, Opera, and the notorious Internet Explorer. Under no circumstances should a developer overlook support for any browser that might be used by even a small fraction of the target audience. Even though Google Chrome and Mozilla Firefox along with Safari have a market share of close to 90-95%, this statistic, depending on the nature of the website and other factors, might prove to be deceiving. Browser preference varies greatly with geographical locations and age demographics. For example, Safari is used considerably less used outside of the United States, and IE is still the browser of choice for an older generation.

Browser Market Share

Google Chrome is the clear market leader with at least 67% market share followed by Firefox trailing at 11%. It is interesting to note that the notorious Internet Explorer, which is very often ignored by developers, still retained a market share of almost 7-10% in 2018. This fact further amplifies the indispensable need for developers to pay heed to cross-browser compatibility. You can further use Google analytics or any other web analytics tool to determine which browsers are preferred by the majority of your website visitors. If the share of a browser or browser version is less than 1%, a developer should consider it a low priority with respect to the ones that are primarily responsible for driving traffic to your website.

The Need for Cross-Browser Compatible CSS

In recent years, we have witnessed the emergence of new, powerful features in CSS, which has given birth to new modern design trends. However, thanks to cross-browser compatibility issues, the majority of these newly introduced powerful CSS3 properties are not universally supported by all major browsers. In absence of fallbacks, these properties are not interpreted by the browser and ignored completely, which can wreak havoc on your website’s design on older, unsupported browsers, especially the dreaded IE. New and exciting features like CSS Grid, Flexbox, CSS Blend Modes, and 3D transforms have pushed the envelope of web design to new heights. However, many developers have been slow to adopt these new rollouts due to their skepticism of cross-browser support. Nobody wants to take the risk of building websites for their clients that most browsers won’t render properly.

Here is a brief section to help you understand CSS properties which are not supported by different web browsers. You can use the Can I Use platform to find the supported and unsupported elements for various browsers and browser versions.

  • CSS properties not supported by the latest version of Internet Explorer (v. 11):
  • CSS properties not supported by the latest versions of Firefox:
  • CSS properties not supported by the latest versions of Google Chrome:
  • CSS properties not supported by the latest versions of Opera:

On top of that, even some HTML elements and attributes are also not supported by some browsers. For example, the form attribute ‘placeholder’ isn’t supported by any version of IE or Edge.

Approaches to Counter Uneven Browser Support

Graceful Degradation vs. Progressive Enhancement

There are two polar opposite development philosophies commonly used by devs for countering uneven browser support and ensuring that users enjoy a degree of consistency across all browsers: graceful degradation and progressive enhancement.

  • Graceful degradation means building your website with full functionality and design features as supported by the latest modern browsers and then gradually coding downwards to provide support for older browsers by stripping away layer after layer, downgrading the enhanced version. The lower version of the website is stripped of its enhanced functionality and UI features but still delivers a baseline version to users.
  • Progressive enhancement is the exact opposite of graceful degradation. At first, a basic/baseline version of the website is created and then, gradually moving upwards, advanced functionality and appearance features are added for modern browsers and newer versions to deliver a feature-rich experience.

Progressive enhancement is widely considered the better approach of the two, as it starts with the basic version and adds further enhancements on top of it later. It guarantees that the website will work in any browser, new or old, and will render advanced versions in all those browsers which support it automatically. It is also considered favorable for testing and crawling by search engines.

What Is CSS Fault Tolerance?

In languages like JavaScript, Ruby, and PHP, the execution is terminated when an error is encountered. On the other hand, CSS is blessed with a feature called “Fault Tolerance.” Whenever the browser comes across a line of CSS code which it cannot interpret or understand, it simply ignores and skips that line of code and jumps to the next. For example, consider the example.

We select the element div and changed the color property to hex value, “#777”

div {
color: #777;
 }   
/*Now look at the following code: */
div {
color: #777;
color: #000; 
}

As we have set the color property value to “#000” from “#ccc”, the second value will be used. Now, if we use an invalid property value, CSS will use its fault tolerance feature and will simply ignore the CSS line that it cannot interpret.

div { 
color: #777;
color: foobar(10); 
}

Because foobar(10) is not a valid CSS value, the browser cannot interpret this line so it simply ignores it. The color value “#777” is retained.

We can leverage this fault tolerance feature of CSS to code fallback properties. With this technique, you don’t need to indulge in the hassle of creating two separate files or writing confusing conditional statements. Your website won’t have to send two separate HTTP requests, first for a modern CSS file and then for IE fix file like in this case:

<link href="modern.css" rel="stylesheet" />
<!--[if lte IE 8]>
    <link href="legacy.css" rel="stylesheet">

Now that we have discussed what cross-browser compatibility issues are, let us now look at ways to overcome this challenge. The two primary methods that we will discuss are:

  1. Browser Detection
  2. Feature Detection

1. Browser Detection

Browser identification is based on detecting the User-Agent String. The NavigatorID.userAgent property returns the user agent string of the current browser. The User Agent request header contains a characteristic string that allows for the identification of the application type, OS, software vendor, or software version, etc.

Syntax
var ua = window.navigator.userAgent;
Where ua stores the user agent string value of the current browser.

For example, here's Firefox UA string:

Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0

Here are the User Agent strings of common web browsers –

Source: https://goo.gl/W6i9B5

The most common approach is to use JavaScript to query the user-agent header:

<!DOCTYPE html>
<html>     
<body>     
<p>Find the name of your browser using User Agent</p>     
<button onclick="myFunction()">Try it</button>     
<p id="demo"></p>     
<script>       
function myFunction() {
    if (navigator.userAgent.indexOf("Chrome") != -1) {
        alert('Your Browser is Google Chrome');
    } else if ((navigator.userAgent.indexOf("Opera") || navigator.userAgent.indexOf('OPR')) != -1) {
        alert('Your Browser is Opera');
    } else if (navigator.userAgent.indexOf("Safari") != -1) {
        alert('Your Browser is Safari');
    } else if (navigator.userAgent.indexOf("Firefox") != -1) {
        alert('Your Browser is Firefox');
    } else if ((navigator.userAgent.indexOf("MSIE") != -1) || (!!document.documentMode == true)) //IF IE > 10     
    {
        alert('Your Browser is Internet Explorer');
    } else {
        alert('Unknown Browser');
    }
} 
</script>       
</body>     
</html>

IE Conditional Statements

Another way to detect or identify the browser is by using Internet Explorer’s conditional comments. This syntax extends the standard HTML comments and is unique to IE.

Conditional statements are Internet Explorer specific CSS rules, i.e. they are only recognized by Internet Explorer and ignored by all other browsers as they treat them as normal comments. Note that in the second case (target other browsers except IE), content is not inside a comment. Therefore, it will be recognized by other browsers.

Target all IE browser versions

<!--[if IE]>
Place content here to target all IE users.
<![endif]-->

Target browsers which are not IE:

<![if !IE]>
Place content here to target all users not using Internet Explorer.
<![endif]>

Target a specific IE version:

<!--[if IE 6]>
Place content here to target all users of IE version 6. 
<![endif]-->

Target IE versions great than or equal to 7: 

<!--[if gte IE 7]>
Place content here to target users of IE7 or higher.
<![endif]-->

Target IE versions less than 7 (i.e. 6 or lower): 

<!--[if lt IE 7]>
Place content here to target users of IE6 or lower (less than 7).
<![endif]-->
<!--[if IE]>
Place content here to target all IE users.
<![endif]-->
  • The biggest drawback of browser detection by user-agents is that the developer needs to constantly keep track of browser support. Moreover, browser detection doesn’t take into account new browser version updates. New browser versions might support for previously unsupported features making your extra code redundant or a new version might remove support for a feature which can wreck your website’s performance.
  • Fallback code will be displayed even if there is a possibility that the new versions of the browser support that feature.
  • Some browsers also use a dummy user agent to mask the original.
  • Many browsers have also started to spoof their user agent strings. Over time, some critical flaws have emerged in User Agent Detection, because of which IE dropped support for it from version 10 and beyond.

Feature Detection

A much better approach to browser detection is called “feature detection.” CSS with feature detection runs a test to determine whether the given feature in question is supported by the current browser, and then conditionally runs different code depending on whether it does or doesn’t. CSS with feature detection involves testing whether a browser is capable of running the given lines of code and depending on the outcome of the test, a specific block of code is run which is supported by the browser, resulting in near perfect consistency and cross-browser compatibility.

This way you can ensure that the browser can deliver a seamless and consistent user experience no matter which browser the user has. A developer doesn’t have to worry about keeping track of cumbersome browser support charts and new version updates. Coupled with the philosophy of progressive enhancement, developers first code the website to deliver the base version supported in all browsers and proceed to add layers depending upon support in new modern browsers. If you do not follow the approach of CSS with feature detection, browsers that don’t support the features won’t display your site as intended and will deliver a poor user experience. Other than delivering cross-browser compatible CSS, it also helps developers to write clean readable CSS by avoiding chaotic multiline fallbacks.

Browser Detection and Feature Detection Comparison

Consider the following test scenario. In both cases, we have a fallback ready for an absence of browser support.

If the browser configuration is known and the user-agent works as intended, i.e. with successful detection, both methods work.

However, when encountered by unknown or incorrect browser configuration, browser detection methods fail completely and are not able to render the page correctly. On the other hand, feature detection handles this challenge in a much more seamless manner. CSS with feature detection runs a test and determines that the browser is capable of displaying Feature A but when it fails to support feature B, it simply relies on a fallback for feature B and successfully renders the page the way developer wanted to.

CSS Feature Detection

@supports Feature Query

There are several tools available in a developers arsenal for achieving cross-browser compatibility like Modernizr and polyfills. Instead of using third-party tools we can achieve the same result by using CSS feature queries. These are conditional rules which allow us to apply different styles to the same element depending on browser support. Feature queries are similar to @media, @document, or @import conditional statements. Unlike the @media query, which uses conditions based on the screen size, a feature query creates conditions based on a browser’s CSS support. If the browser comprehends and supports the property inside the feature query, then all the CSS style rules inside the query brackets will be applied. Otherwise, it is ignored and skipped over.

@supports Rule

@supports is a conditional group rule which tests whether the browser supports CSS property value pairs or not. The browser performs cross-browser testing to check whether or not a certain CSS property or value is supported. The result determines whether or not a block of CSS code is applied. The syntax is similar to a media query, but instead of a media query, we set a CSS declaration as the test condition for validating cross-browser compatible CSS. The browser executes the CSS styling based on that condition.

Syntax:

@supports(test condition) {
    /* apply rules */
}

Example:

Consider the following example involving the vmax width property:

.container {
 height: 100%;
}

@supports (height: 100vmax) {
 .container {
      height: 100vmax;
 }
}

@supports queries test whether vmax is supported by the user’s browser or not. If it is supported, the height of the container will be set to 100vmax, otherwise, if the feature is unsupported, the height of the container will be set to 100%.

Another example to test whether a browser supports CSS grid or not.

div {
  background-color: yellow;
}

@supports (display:grid) {
  div {
    background-color: green;
  }
}

In browsers that support the grid feature, the background color of div element will be set to green (because the conditional resolves to true), while for browsers that do not support grid, the background of div will be set to yellow.

@supports Operators

We can use multiple logical operators to supercharge the @supports feature to create complex condition tests.

(a). not operator

The “not” operator can be used with @supports to check for No Support.

@supports not (display: flex) {
div { display: inline-block; } /* alternative style if display:flex is not supported */
}
(b). And operator

The “And” operator can be used to check multiple test conditions all at once. The result is true only if all conditions are true. If even a single condition is false then the resulting boolean is also false.

@supports (mix-blend-mode: overlay) and 
  (background: linear-gradient(rgb(45, 145, 245), rgb(20,120,220))) {

  .wrapper {
    background: linear-gradient(rgb(45, 145, 245), rgb(20, 120, 220));
  }

  .wrapper img {
    mix-blend-mode: overlay;
  }

}
(c). Or operator

The “Or” operator can be used to check whether at least one condition out of many is true or not. The result is false only if all the conditions are false.

@supports (display: -webkit-flex) or
          (display: -moz-flex) or
          (display: flex) {

    section {
      display: -webkit-flex;
      display: -moz-flex;
      display: flex;
      float: none;
    }
}

Supports Detection Using Javascript

Feature queries can also be used with JavaScript. JavaScript provides the CSS.supports() method which can be implemented in two ways:

  1. CSS.supports(propertyName. value);
    1. Example: CSS.supports("text-decoration-style", "blink");
  2. CSS.supports(testCondition);
    1. Example: CSS.supports("display: flex");

A bit off topic, but if you are struggling with memory leaks related to JavaScript then I have also written a detailed blog on eradicating memory leaks in JavaScript.

Using CSS @supports to Create a Fallback for the CSS Grid

We can now utilize feature queries to create a fully functional mini layout for a gallery or portfolio section. We can do this via the progressive enhancement approach by creating a basic layout version first and then adding browser support for flexbox and CSS grid.

<!DOCTYPE html>
<html>
<head>
<style>
  body {
text-align: center;
  }

  .grid-wrapper {
display: inline-block;
  }

  .grid-card {
width: 18em;
display: inline-block;
margin: 1.5em 1em;
text-align: left;
  }

  .grid-card-img {
width: 100%;

  }

  @supports (display:flex) {
.grid-wrapper {
  display: flex;
  flex-wrap: wrap;
  margin: -1.5em 0 1.5em -1em;
  justify-content: space-around;
}

.grid-card {
  padding: 1.5em 0 0 1em;
  flex: 1 0 18em;
  max-width: 18em;
  width: auto;
  margin: initial;
}
  }

  @supports (display:grid) {
.grid-wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18em, 1fr));
  grid-gap: 0.8em;
  margin: initial;
}

.grid-card {
  padding: initial;
  max-width: none;
}
  }
</style>
</head>
<body>

  <div class="grid-wrapper">
<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=40">
  <h2>Grid Card 1</h2>
  <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam amet laudantium maxime suscipit. Ratione, eos.</p>
</div>
<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=41">
  <h2>Grid Card 2</h2>
  <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Deleniti veniam quod consectetur mollitia quam voluptas.</p>
</div>

<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=42">
  <h2>Grid Card 3</h2>
  <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam ipsam accusantium voluptas! Provident, magnam non!</p>
</div>

<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=43">
  <h2>Grid Card 4</h2>
  <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ratione deleniti praesentium delectus quas maiores perferendis!</p>
</div>

<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=44">
  <h2>Grid Card 5</h2>
  <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Architecto cum quia tempore totam a! Explicabo?</p>
</div>
<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=45">
  <h2>Grid Card 6</h2>
  <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae recusandae quidem, nihil maxime dicta suscipit.</p>
</div>
<div class="grid-card">
  <img class="grid-card-img" src="https://unsplash.it/640/425?image=46">
  <h2>Grid Card 7</h2>
  <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Impedit odit saepe, facere iste adipisci aliquam?</p>
</div>

  </div>
</body>
</html>

Results:




Browser Support for Feature Queries

One sticking point with using the @supports feature queries is the lack of browser support in some old archaic browsers.

No version of Internet Explorer, including version 11, supports feature queries. Safari rolled out support for feature queries but only in recent versions. But it must be noted that as CSS degrades gracefully if a browser does not recognize the @supports rule it will simply ignore the entire block of code without wrecking the web page. The best approach, therefore, is to first code your base CSS styles which every browser can handle. Then use @supports feature queries to override that base styling with more advanced functionalities and appearance for modern browsers that can handle those features.

Some developers looking for IE browser support prefer to use tools like Modernizr, Post-CSS, and other polyfills to address their cross-browser compatibility needs.

CSS Feature Detection Using Modernizr

Modernizr is an extremely useful JavaScript library that helps to automatically detect the availability of next-generation HTML and CSS features in a user’s browser. Rather than relying upon user-agent sniffing, Modernizr depends on CSS feature detection to allow developers to deliver specific user experiences based on a user’s browser capability. It runs a test on the user’s browser to check whether it supports a particular feature or not. If the feature is deemed to be “unsupported” a developer can deliver an appropriate script or function to imitate the unsupported feature. Unlike CSS feature detection using @supports feature queries, Modernizr also allows for the building of custom tests.

Setting Up Modernizr

Older versions of Modernizr gave you two options to download the file: Development and Production version. However, in the recent version of Modernizr, a single development version, modernizr.js, is no longer available.

  1. Visit the Download page to custom select only the features you want in your project. This helps reduce file size and boost loading speed of the webpage.
  2. Insert the file inside the <head> section. For example: <script src=”modernizr.js type=”text/javascript></script>
  3. Modernizr adds many CSS classes on the root <html> tag by default. These classes are generated by Modernizr and vary depending on the browser’s capabilities. Classes are added for each feature when it is supported, and added with a no- prefix when it is not (e.g. .feature  or  .no-feature ).
    1. For example: <html class=” js flexbox flexboxlegacy csstransforms no-csstransforms3d csstransitions”>
  4. Lastly, we also need to add a no-js class to the <html>tag, <html class=”no-js”>. This class is necessary, so if the user’s browser is running without the JavaScript enabled we can add necessary fallback using this class. If it does support JS, then Modernizr will replace this class with just JS.

Consider the case of Modernizr’s detection for CSS gradients. Depending on the feature detection for the the given browser, the result will be either <html class=”cssgradients”> or <html class=”no-cssgradients”>. We can use progressive enhancement methodology to target both cases easily thanks to Modernizr classes.

.no-cssgradients .header {
  background: url("https://unsplash.it/640/425?image=44");
}

.cssgradients .header {
 background-image: url("https://unsplash.it/640/425?image=44"), linear-gradient(red, yellow);
}

Modernizr Feature Detection Using JavaScript

We can also use Modernizr with JavaScript to test the browser feature with the following syntax:-

// Test for flexbox
if (Modernizr.flexbox) {
 console.log('flexbox is available.');
 /* Script A */
} else {
 console.log('flexbox is not available.');
 /* Script B */
}

Modernizr vs. Feature Queries

Modernizr is supported by practically all browsers imaginable unlike feature queries, which are not supported by any Internet Explorer version, including 11. Excluding IE, feature queries have already been widely implemented ,covering 91.68% of global users. Unlike feature queries which are natively supported by browsers, Modernizr needs to be first downloaded and executed in JavaScript, which decreases page load speeds and can affect ranking son SERPs. Modernizr still does not cover the complete spectrum of CSS properties like @support queries can.

Cross-Browser Testing Is Indispensable

It is inconceivable to achieve cross-browser compatibility by using feature detection with feature queries or Modernizr alone, if a developer can’t even detect rendering issues across different browsers and browser versions. The more browsers and browser versions you validate your website on, the more robust your UI becomes. Not only does this help to deliver a seamless experience to your customers, but it also helps in delivering a sense of accomplishment and relief to the developers. This is why using a cross-browser testing tool is vital. Without cross browser testing, developers won’t be able to validate whether changes they have made to achieve browser compatibility are working as intended or not. 

Conclusion

Cross-browser compatibility, without a doubt, is an indispensable dimension of web development which can no longer be ignored. However, contrary to popular belief, websites don’t need to look and function exactly the same on each browser. It is crucial to comprehend that full, 100% compatibility is simply impossible to achieve. Therefore, the key goal of every developer should be to make their websites accessible across different browsers and provide a pleasant, seamless viewing experience to as many users as pragmatically possible. So far, the developer community has relied on JavaScript, especially Modernizr, to resolve their cross-browser compatibility issues. But in recent times CSS feature detection has emerged as a viable, lightweight, and easy to use an alternative solution that is finding its way into the mainstream and gaining popularity among developers.

Topics:
css ,web dev ,cross-browser compatability ,css tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}