How CSS Resolves Style Conflicts

August 29, 2019

Style conflicts happen

Let's say you and the other CSS developer on your team are creating button styles.

Your design team passed you the final sketch/figma files and you find out they have a thing for smooth pill-shaped buttons.

a pill-shaped button

So you and your CSS pal code the following to match those button styles.

<p> CODE: https://gist.github.com/jginbound/1b1135dc7d29ce04789c670b8f56ac74.js</p>

Looking good!

However, the months go by and your team's stylesheet gets bloated with superfluous code. Somewhere in the stylesheet, a small, innocent change in the CSS causes a massive style override, and all of the buttons now have a blue text color:

a text-color override

How did this happen? It turns out that after investigating the git logs, a newly hired dev on the team accidentally overrode the button styles in order to make all anchor links of a blue color.

Take a look at line 18 in the stylesheet below. The !important tag was used to "brute-force" the color on all anchor-links from white to rgba(0, 50, 200, 0.8).

<p> CODE: https://gist.github.com/jginbound/5e34ee4320f48ef898e5f46b3ada5436.js</p>

Because !important tags can override any style declaration, they are considered a bad practice in writing CSS code.

Now take a look at this simple example of a style-conflict:

The styles below override the styles above

At first, the paragraphs is styled with a red color and font-size of 1rem. But right below it, the paragraphs are later styled with a new color of purple and font-size of 2rem.

Which style wins in the end? How does CSS go about enforcing which styles get applied eventually?

CSS uses what's known as the "Cascade", which is an algorithm for figuring out how styles will be applied to a webpage once it has loaded in the browser. While figuring out how styles will be applied to HTML elements, the cascade algorithm also resolves any style conflicts along the way.

First, let's get some terminology straight

CSS is a language whose purpose is to style HTML elements that's already stored in memory and displayed in the browser.

1. This is an html element

this is a paragraph element

2. A CSS Selector is how CSS identifies and targets an HTML element which will then receive style declarations

Once CSS selects an element, it will receive a set of style-rules. The "p" in the image below is a type of CSS selector because it targets the paragraph element, which will then receive style declarations, or CSS-rules.

The "p" element is a CSS selector because CSS targets it element to later style it.

3. A CSS-rule is a style declaration

This is a CSS rule:

<p> CODE: https://gist.github.com/jginbound/e0acf0f63260be4f3a892f851244b426.js</p>

CSS can apply more than one CSS-rule for any selector. Like this:

<p> CODE: https://gist.github.com/jginbound/e664cff3ba65d296aeb58984886a9097.js</p>

Now back to our example from above.

multiple css-rules cause style conflicts

The paragraphs are purple with a bigger font-size. But we wanted them to be red and at 1rem to begin with- how does CSS decide which style takes precedence in the end?

The answer lies in the CSS cascade algorithm, which parses every CSS rule in the stylesheet from top to bottom.

For starters, CSS looks at the last style declaration in the stylesheet and resolves it as the "winning style" in the event that an HTML element has a style conflict. So in our paragraph example above, the purple color style with a font-size 2rem appears further down the stylesheet and therefore beats out the red and 1rem style rules.

This way of parsing styles from the top of a stylesheet to the bottom is known as the "cascade" because it resembles a waterfall that starts from the top and works its way down the source-order of the stylesheet.

That's a nice looking cascade...

But there's more to the cascade algorithm than just looking at the source order of CSS rules

As you'll see, the source-order of the cascade algorithm is just one out of two other important components in the algorithm.

Take a look at this example:

There are other aspects beyond source-order that affect the way style conflicts are resolved in the cascade algorithm

The paragraph color is black and has a font-size of 1.25. Shouldn't it be purple with a font-size of 2rem? Based on our understanding of how CSS applies styles based on where they appear in the source-order, the purple color style and font-size of 2rem appear later and should therefore be... purple!

So... how does the cascade algorithm really work?

We'll get there, I promise. In essence, CSS takes the stylesheet in question, parses it, and does more than just look at source-order. It asks the following questions:

Question 1: Are there any !important tags to look out for?

!important tags are really powerful- and with great power comes great (sometimes grave) consequences for your stylesheet. They can override any CSS-rule that you declare for any selector, regardless of where it is in the source-order.

Take a look at our button dilemma from before. This code shows us that even though the styles on line 9 appear later in the source-order, the CSS-rule on line 1 will take precedence, and that's due to the !important tag.

<p> CODE: https://gist.github.com/jginbound/b04a873e10d7b52ab5650bf0ddae0005.js</p>

Any CSS-rule with an !important tag will automatically take precedence in the end, regardless of where it is in the source-order of the stylesheet. End of story.

An !important tag is like that fancy VIP card that you flash at security to get in the front of the line. All of the other CSS selectors will be staring you down in envy while muttering underneath their breath, "psh there goes the !important tag, overriding everyone else..."

And while you flash that !important card, security (or the CSS parser in this case) will high-five you on your way in.

!important is a pretty big deal in the world of CSS

Should you use !important tags in your CSS code? Please don't. Look at what happened to our junior dev from our example above. !important tags will override any style on a selector and can cause major bugs in your code.

Here's what MDN (Mozilla Developer Network) has to say on the matter of !important tags:

Using !important, however, is bad practice and should be avoided because it makes debugging more difficult by breaking the natural cascading in your stylesheets.

Thank you, MDN- so we now understand that any CSS style rule with an !important tag will override any selector's style regardless.

But what if there aren't any !important tags in the stylesheet?

Take a look at this example from earlier:

<p> CODE: https://gist.github.com/jginbound/8fd16d45128737a7f2721eb14457a3e8.js</p>

This stylesheet does not have any !important tags.

At first glance, you'd be tempted to think that the paragraphs will be purple with a size of 2rem. But that's not the case as the styles found in lines 4-6 take precedence over the styles in lines 10-11.

This brings up the next question the cascade algorithm asks:

Question 2: How specific are the selectors?

CSS has several ways of referencing selectors.

We can go the plain-vanilla way and just reference its HTML element, like this:

this is the least specific way of targeting a selector in CSS

Or with its class and/or pseudo-class like this:

We're getting more specific by referencing the selector's class and/or pseudo-class

We can dig deeper into specificity and reference a selector with its ID attribute, like this:

This is an ID selector

Lastly, we can declare CSS-rules within the HTML file. These CSS-rules are called "inline-styles" and they have the highest level of specificity:

This inline style declared CSS-rules from within the HTML file

Inline-styles are very specific and tend to override almost any other CSS-rule declared in a stylesheet. Take a look at this example:

The inline-styles found in the <p> element override the styles declared in the CSS stylesheet. Now the paragraph is red with a font-size of 3rem.
The level of specificity of a CSS-rule is determined by the selector that targets it.

Specificity is measured in order of increasing importance:

  1. HTML elements or pseudo-elements (least specific)
  2. Classes or pseudo-classes (more specific)
  3. ID attributes (even more specific than)
  4. Inline-styles (most specific)

This lonesome <p> in line 1 isn't as specific as the one on line 6:

<p> CODE: https://gist.github.com/jginbound/9768349a2bfb17b7b1ef2fcb674de16e.js</p>

So is the level of specificity measured in some way?

I'm glad you asked! The CSS cascading-algorithm uses a point system to measure how specific a selector is.

The more specific a selector is, the more weight it receives for winning out its style-rule(s) in the end.

Again- CSS uses a point-based system to quantify how specific a selector is.

This is the CSS-specificity point system: starting from the thousands and ending at the ones slot.

Let's start with the thousands slot

The thousands slot is the highest amount in the point system. Any CSS-rule that's declared as an inline-style wins the 1000 points, and is automatically awarded the style in the end.

Take a look again:

The inline style overrides all of the other style-rules due to its higher specificity value as an inline-style.

Because this CSS declaration was declared as an inline-style, it wins one spot in the thousands slot:

The thousands slot is reserved for inline-styles
But please be aware that !important tags can override inline styles

Even though inline styles are the most specific of selectors, they can be overridden by the !important tag. Take a look:

!important tags will override inline styles

Next on the list is the hundreds slot

Any CSS selector with an ID attribute gets a score in the hundred slot, just like this:

<p> CODE: https://gist.github.com/jginbound/d66b77c9272a8fb8377be9b7cd17f75b.js</p>

The ID attribute on line 1 has a specificity level of 100.

The hundreds slot is reserved for ID selectors

Next is the tens slot

Any selector that's invoked as a class, pseudo-class, or attribute will receive a spot in the tens slot.

Take a look at the markup found in this form element. Do you see any class selectors here?

This form element has class attributes among other ones

These are class selectors, each which will be awarded 10 points on the specificity scale:

These class selectors are each worth 10 points on the specificity scale

This is a pseudo-class selector which will also be awarded 10 points:

Ten points for this pseudo-class selector

Lastly, this attribute selector will also be awarded 10 points on the specificity scale:

Ten points for this attribute selector

Phew. All of this sounds overly theoretical. The best way to understand specificity is with simple examples. So let's take a look at one:

<p> CODE: https://gist.github.com/jginbound/f0faba6682425a290e6e2ddb24cf1ed9.js</p>

The more specific a selector, the more weight it receives for applying its styles in the end

The anchor link style gets overriden by the more specific .btn class, which then gets overriden by the even more specific .btn#cta selector.

Now, what happens if there are no !important tags and the specificity values of the selectors are the same?

Well in this case the CSS parses asks the final question in its algorithm:

Question #3: Where is the style applied in the source order of the stylesheet?

This one is really simple. If there are no !important tags and the specificity level is the same for the selector, then the selector that appears later in the stylesheet is the one who wins the style-rule in the end.

As MDN states, "Later rules win over earlier rules." Take a look:

Exercise time!

Can you calculate the specificity score of the following selectors:

Solutions

Here's what I got:

  • p -> 1
  • p#paragraph -> 101
  • #button.btn:hover -> 120

If you have to take one thing away from this post

Please, please do not use !important tags in your CSS-rules. They will make it hard for other developers to debug your code as your stylesheets get larger in size.

Remember your rules to specificity and you'll be set to handle any styling conflicts along the way!

What did you think about what you've just read?

Wanna start a conversation?

Get at me!