Thursday, June 08, 2017

[CSS] How are conflicting styles resolved?

If you have worked in CSS, then you’ll know that you can assign a CSS property using the syntax:
property-name: value
For example, if you have a <span> tag with ID ‘content’, for which you want to assign the color green, you’d add this in your CSS file:
#content { color: green; }
There are other ways you can specify the same property:
span#content {color: green;}, and
.content {color: green;} in combination with <span class="content">lorem ipsum</span>

Here's the interview question

What happens though when you have multiple instances of the same property being set & they all apply to the same HTML tag too? Here’s an example:
Consider this tag,
<span id="content" style="color: blue;">some content</span>
while the CSS definition in the associated CSS file that can match the element is:
#content {color: green;}
Since multiple styles match, which one will the browser render? Answer: The text in the span element will be rendered in blue.
Why? Why did the browser decide to apply blue? As per the CSS spec, there are two aspects to be considered when deciding which style a browser will apply among competing styles. Resolving these two aspects tells the browser which competing style should win. They are: 1) Cascading order, & 2) Specificity. We'll first look at Cascading order and later in the post, Specificity.

Cascading order

In English, the term "cascade" is used to describe a process where there are multiple steps. For example, a cascading waterfall is one in which water flows down multiple steps.
If that is the case, what does "Cascading Style Sheets" mean? What steps are there in CSS? It turns out there are multiple ways through which style definitions for a web page can be assigned. They are: author, user & user agent.
  • Author styles are those which all software developers know - they are created by the authors of the web page as CSS files or style attributes in HTML tags.
  • User styles are those styles which users of web browsers can configure on their browser. For example, users can configure that browsers render particular fonts by replacing it with other fonts - this is particular useful from an accessibility standpoint.
  • User agent styles are those styles that are provided by default by the browser. For example, if no colour information is provided, then text is rendered black on a white background by default - this is an example of user agent styling.
The "cascade" in Cascading Style Sheets flows thus: If there are conflicts in property definitions across user, author or user agent style definitions, then the precedence is as follows:
Author > User > User agent

Example 1

In this example, we’re going to determine what happens if a user CSS file has a definition that conflicts with a definition in the user agent's default CSS file. The user agent we’re going to use is Internet Explorer. It already has a user agent CSS file (this is why a plain HTML file without any styling will render black text on a white background.) We will now change the way IE renders text color inside tags by default by providing a user CSS file.
Create a file by name, my_style.css. The content of this file is just this one line:
div{color:red}
We will now tell IE to use this file from now on for all web pages. The way to do so is this:
  • Open Internet Explorer
  • Click on the Tools menu & choose Internet Options
  • Click on the General tab & choose Accessibility. You should get a screen like this:
  • Under the User style sheet section, enable the Format documents using my style sheet checkbox.
  • Now click Browse… under the same checkbox and choose my_style.css.
  • Restart Internet Explorer.
We now need to create an HTML file that we can load into the browser to test that IE uses the my_style.css. Create a file by name, test_my_style.html. The content of this file is:
<html>
  <head>
     <title>Testing user styles</title>
  </head>
  <body>
      <div>This is a test file to test user styles.</div>
  </body>
</html>
Opening this file in Internet Explorer gives us this output:
What happened here? The user agent, by default, will render text inside tags as black-colored text. Our user file, my_style.css, overrode that, thus creating a conflict. IE followed the CSS spec which states that User CSS property definitions have priority over user agent CSS property definitions and rendered the text in red color.

Example 2

What happens if we introduce a further conflict by having an author-defined CSS file? For this, we will create another CSS file, author_style.css, where we will provide the following definition:
div {color:blue}
We will also change test_my_style.html to include author_style.css as follows.
<html>
  <head>
    <title>Testing user styles</title>
    <link href="author_style.css" rel="stylesheet"></link>
  </head>
  <body>
    <div>This is a test file to test user styles.</div>
  </body>
</html>
Opening this file in Internet Explorer gives us this output:
What happened here? The user agent, by default, will render text inside tags as black-colored text. Our user file, my_style.css, overrode that, thus creating a conflict. The author’s CSS file, author_style.css, overrode that even further setting up another conflict. IE followed the CSS spec which states that Author CSS property definitions have priority over all other CSS property definitions and rendered the text in blue color.

An exception

The only exception to the cascade order above is if the property definition is marked as !important, in which case that definitions take precedence over other definitions for that property. There are no property definitions marked !important in the user agent CSS file.
Let’s look at an example: We will reuse the same files as before, but we will change my_style.css to this:
div{color:red !important}
Now if we open our test_my_style.html in IE, we get this output:
What happened here? The user agent, by default, will render text inside tags as black-colored text. Our user file, my_style.css, overrode that, thus creating a conflict. The author’s CSS file, author_style.css, overrode that even further setting up another conflict. However, IE noticed the !important in my_style.css and followed the CSS spec which states that User CSS property definitions with !important have priority over all other CSS property definitions and rendered the text in red color.

Specificity

The approach mentioned above will still cause conflicts since one of the user/author stylesheets can have conflicting style definitions. To resolve this, CSS provides another mechanism which browsers can use - specificity. While there isn’t a definition of specificity in the spec, my definition is: Specificity determines how specific the style definition is. Here, specific means how many HTML elements does the CSS selector match - the less elements it matches, the more specific it is, the more elements it matches, the less specific it is.

Calculation of specificity

The calculation of specificity is done in the following manner:
Assume there are four numbers separated by commas, and their initial values are zero:
0,0,0,0
The first number represents the presence of a style attribute in the element's HTML. If a style attribute is present, then the first number becomes 1, otherwise 0.
The second number represents the number of id attributes in the selector.
The third number represents the number of attributes and pseudo-classes in the selector.
The fourth number represents the number of element names and pseudo-elements in the selector.
Unlike in the decimal system, if a number reaches the value 10, then it does not carry over to the preceding number. Thus, specificity values like 0,10,0,9 are perfectly valid.
Now that we know what specificity is, let’s take a look at some example CSS definitions, and try to understand what specificity value they evaluate to:
Example 1: div.content {color:red}. It is not a style attribute in a HTML tag, nor does it have any HTML IDs mentioned in the selector. Thus the first two numbers are 0,0. It has a class attribute value mentioned(.content), and it also has a HTML element mentioned (div). Thus, the final two values of the specificity are 1,1. Hence it's final specificity value is 0,0,1,1.
Example 2: #content::first-letter. It is not a style attribute in a HTML tag, but it has a HTML ID mentioned in the selector. Thus the first two numbers are 0,1. It has a pseudo-element mentioned(::first-letter), and it doesn't have any HTML elements mentioned. Thus, the final two values of the specificity are 0,1. Hence it’s specificity value is 0,1,0,1.
Example 3: div[data-name=Tom][data-url=/member/1]. It is not a style attribute in a HTML tag, nor does it have any HTML IDs mentioned in the selector. Thus the first two numbers are 0,0. It has two attributes mentioned(data-name & data-url), and it has 1 HTML element mentioned (div). Thus, the final two values of the specificity are 2,1. Hence it’s specificity value is 0,0,2,1.

Resolving conflicts with specificity

Given two specificity values, you can compare them to find out which one is greater or lesser. A specificity value is greater than another specificity value if the first specificity’s first number is greater than the second specificity’s first number. In case the first number of both values are the same, then the browser moves on to compare the second number of both specificity values, and so on.
Here are some examples:
1,0,0,0 is greater than 0,10,0,0
0,10,0,0 is greater than 0,0,20,0
How is specificity helpful in resolving conflicts? As per the CSS spec, browsers are supposed to resolve conflicts by choosing those CSS definitions that have a higher specificity.

Example

Let’s take the example in the interview question above:
In the HTML, we have:
<span id="content" style="color: blue;">some content</span>
while in the CSS file, we have:
#content {color: green;}
Constructing the specificity for the style definition in the HTML style attribute, we get:
1,0,0,0
Constructing the specificity for the CSS style definition, we get:
0,1,0,0
Because the first specificity value is greater than the second, the style definition in the style attribute of the HTML tag wins.

Is it possible to still have conflicts?

Yes. For example, there could be two definitions in an author CSS file which target the same elements and have the same specificity. In such cases, the CSS spec says that browsers can use the definition that appears later.
An example:
Let’s say that we have two CSS definitions as below:
div {color:blue};
div {color:red};
for this HTML,
<html>
  <head>
     <title>Testing user styles</title>
  </head>
  <body>
     <div>This is a test file to test user styles.</div>
  </body>
</html>
Both CSS definitions evaluate to a value of 0,0,0,1.
In this case, the browser will simply render the text in red.