CSS — Style Override Technique

Photo by Nick Karvounis on Unsplash

Are you run into a situation that you have no idea why your CSS selectors are not working? Or you don’t know which is the best practice to override the existing style on an element with your custom style. This post is for you.

To understand what I am trying to explain here, let’s assume that you already know about CSS basics and CSS selectors.

There are three factors to consider regarding the cascading process, listed here in increasing order of importance. Later ones overrule earlier ones:

  1. Source order
  2. Specificity
  3. Importance

We will look at these to see how browsers figure out exactly what CSS should be applied.

Source order

Selector matches cascade from the top down. If you have more than one rule, which has exactly the same weight applies to an element, then the one that comes last in the CSS will win. You can think of this as rules which are nearer the element itself, overwriting early ones until the last one wins and gets to style the element.

Note: Be aware of the priority of CSS types in basics as below. (4 has the highest priority)

1. Browser Defaults.
2. External Style Sheets (Linked or Imported).
3. Internal Style Sheets (Embedded).
4. Inline Styles.

Check the simple example below:

p { color: red  }
p { color: blue }
----
<p>Hello, I'm blue and not red</p>

One stupid question, the content inside the p tag will be blue or red as a result? Blue, right? Easy game. Let’s move on to the next exciting factor — specificity.

Specificity

Once you understand the fact that source order matters, at some point, you will run into a situation where you know that a rule comes later in the stylesheet, but an earlier, conflicting rule is applied, it’s because the earlier rule has a higher specificity — it is more specific, and therefore is being chosen by the browser as the one that should style the element.

The browser will calculate specificity as follows:

  • Count the number of ID selectors (= a).
  • Count the number of the class selector, attribute selector, and pseudo-class selector (= b).
  • Count the number of element selectors or pseudo-element selectors (= c).

Concatenating the three numbers (a, b, c) gives the specificity. Essentially a value in points is awarded to different types of selectors, and adding these up gives you the weight of that particular selector, which can then be assessed against other potential matches. The amount of specificity a selector has is measured using four different values, which can be thought of as hundreds, tens, and ones.

These are some exception rules:

  • Inline style — No selector, with a rule inside an element’s style attribute. So their specificity is always 1000.
  • The universal selector (*), combinators (+, >, ~, ' '), and negation pseudo-class (:not) do not affect specificity.
  • Attribute selectors and pseudo-classes have the same weight as a class selector.

So, is it too complicated? To help you understand calculating specificity better, let’s go to the “specificity wars” as an example. Suppose the war have four characters with the Sith power (specificity) for each as below:

  1. A stormtrooper (element selector) is less powerful than Darth Maul (class selector).
  2. Darth Maul is less powerful than Darth Vader (ID selector).
  3. Darth Vader is less powerful than the Emperor (style attribute).

Then the Sith power of the combination of characters will calculate as follows:

Image from this
More examples to test your understanding.

Note: This has only been an approximate example for ease of understanding. In actuality, each selector type has its own level of specificity that cannot be overwritten by selectors with a lower specificity level. For example, a million class selectors combined would not be able to overwrite the rules of one id selector.

The Death Star has the highest Sith power. It blows up everything! So let’s move to the most powerful factor in the cascading process — importance.

Importance

There is a special piece of CSS that you can use to overrule all of the above calculations. However, you should be very careful with using it — !important. It is used to make a particular property and value the most specific thing, thus overriding the normal rules of the cascade.

Check the simple example below to understand more:

#winning {
background-color: red;
border: 1px solid black;
}

.better {
background-color: gray;
border: none !important;
}

p {
background-color: blue;
color: white;
padding: 5px;
}
-----
<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>
cascading result.

Let’s walk through this to see what’s happening:

  1. You’ll see that the third rule’s color and padding values have been applied, but the background-color hasn't. Why? All three should surely apply because rules later in the source order generally override earlier rules. However, the rules above it win because class selectors have higher specificity than element selectors.
  2. Both elements have an class of better, but the 2nd one has an id of winning too. The 2nd element does get the red background color, but no border. Why? Because of the !important declaration in the second rule — including this after border: none means that this declaration will win over the border value in the previous rule, even though the ID has higher specificity.

Note: The only way to override this !important declaration would be to include another !important declaration on a declaration with the same specificity later in the source order, or one with higher specificity.

It is useful to know that !important exists so that you know what it is when you come across it in other people's code. However, I strongly recommend that you never use it unless you absolutely have to. !important changes the way the cascade normally works, making debugging CSS problems really hard to work out, especially in a large stylesheet.

In large web projects where multiple people work on the same style files, bandaging issues with the easy choice of applying!important rules will cause the problems to grow further as the development continues. It is hard for users to apply their styles for better readability or accessibility while viewing your website. Not only that, but also the more!important rules you use, the more difficult your CSS code will become to read and maintain.

You may have to use one situation when working with remote (third party) style files and JS scripts on your site, and you need to modify the styles to suit your needs. Also, the!important rule comes in handy when overriding the style changes made by JavaScript on the fly. It may not be considered a must, but sometimes, applying a different style to a smaller group of a certain class of elements will be possible with !important. For example, you have a .button class that you use in different places across your site, but it is also affected by the style of certain elements containing it. In this case, applying!important to the .button class to preserve its style across different sections might be pretty helpful.

What’s next

If you understood most of this article, then well done — you’ve started getting familiar with the fundamental mechanics of CSS. Next, I strongly recommend you should continue on these topics to getting started with the essential thing of a web developer — CSS.

References

Software Engineer (C#/.NET, JavaScript, Microservices, K8s, Azure)