Understanding Specificity in CSS

Jess Mitchell

Specificity in CSS refers to the how browsers determine the importance, relevance, and “seniority” of CSS styles. Here we’ll look at all the CSS styling types and use the company analogy to understand how competing styles get resolved.

CSS Selector Overview

There are multiple ways to select the elements you’re trying to style in CSS. Let’s start by reviewing all the options.

  • Type Selectors: Select your intended element by using its element type. For example, to select all your <p> tags, use p in your CSS style sheet.
  • Pseudo-Elements: As the name suggests, pseudo-elements are not elements themselves but allow you to select part of your HTML relative to another selector. For example, select the first letter of each paragraph with p::first-letter.
  • Class Selectors: Elements can have multiple classes set on them to be selected in your CSS style sheet. For example, <h1 class='header'> can be selected with .header. Multiple elements can have the same class applied to them.
  • Attribute Selectors: Select elements that have a specific type of attribute applied to them. For example, select inputs that accept numbers only with input[type='number'].
  • Pseudo-Classes: Select elements based on the CSS state they’re in. For example, style the hovered state of a button with button:hover. Check out these previous tutorials on the :target, :hover and :active pseudo-classes to learn more.
  • ID Selectors: Select a specific element with its unique ID. There can only be one element with each ID, whereas classes can be applied to multiple elements. For example, select <h1 id='mainHeader'> with #mainHeader.
  • Inline Styles: Inline styles are applied to elements directly with the style attribute so you don’t actually use any CSS selectors. For example, you can make your header font color blue directly with <h1 style='color: blue;'>

CSS Selectors and Their “Weights”

Each type of selector listed above has a weight. All of these can be divided into four main groups:

  • lowest weight: type and pseudo-element selectors
  • low weight: class, attribute, and pseudo-class selectors
  • medium weight: ID selectors
  • high weight: inline styling

If styles of differing weights are applied to the same element, the styling with the higher weight will be applied. If styles of even weights are applied, the styles that come last (nearest to the end of your style sheet) will be applied. This is due to the “cascading” effect of CSS (Cascading Style Sheets).

For any element being styled, the last applicable block of styling with the highest weight will be applied to your element. Inline styling will trump CSS selector styling, though. 💅

When two selectors of the same weight are applied to an element, it counts as 2x the weight. So, for example, an element selected with two classes will have a higher weight than just one in your CSS.

.gator.cayman { // two classes
  color: purple;
}

.gator { // one class
  color: purple;
}

The main issue most of us web developers will face at some point is having multiple styles that interfere with each other. If you use a CSS framework, like Material UI, you may be trying to override some default styles. Alternately, you could have an old (and disorganized 🙈) styles.css file where you keep having to increase specificity because it’s not clear why your styles aren’t getting applied as expected.

Like anything in web development, understanding your tools well will lead to them being used more accurately. Let’s take a look at how different combinations of selectors impact specificity and some tricks to get your styles applied as intended. 🤓

The Problem with Competing Selectors

Understanding that different selectors have different weights is crucial for getting your CSS organized. But what if it’s not clear what has a higher weight?

Let’s say we have a paragraph that has two completing blocks of CSS: one with three matching classes and another block with a type attribute and two matching classes.

For example, let’s take this input with three classes and a number type attribute applied to it.

<input type='number' class='gator cayman reptile'>

If we apply these competing selectors (three matching classes vs. two matching classes and an attribute), which one will get applied?

.gator.cayman.reptile {
  color: purple;
}

[type='number'].gator.cayman {
  color: green;
}

In this case, the “weight” of both blocks is completely even. Attribute selectors and class selectors have the same weight and each block has three of them in total. Since they’re even, we rely on the cascading effect of CSS. The last one gets applied and the input font color will be green.🐊

This gets a little more complicated when you have selectors of different weights getting mixed, though.

Let’s update our input to have an ID, which has a higher weight than classes and attributes.

<input
  type='number'
  id='gatorInput'
  class='gator cayman reptile'>

If we have competing styles again and use the ID for one and the classes/attribute for the other, which will get applied?

#gatorInput {
  color: purple;
}

[type='number'].gator.cayman.reptile {
  color: green;
}

In the first block, only one ID is being used to apply the styling. In the second block there are three classes and an attribute selector, and it comes last. Even though there are more selectors in the second block, the ID selector has a higher weight and the text will be purple! 💥

Selectors with a higher weight will always trump those with a smaller weight, even if there are multiple selectors of a lower weight. 🏋

Selector Weight Trick

One trick (okay, hack 💩) in CSS when you just need a little more weight is repeating a class in the same block of CSS.

input.gator.gator {
  color: purple;
}

input.gator {
  color: green;
}

It might surprise you to know that the first block has a weight of two classes and the second block has a weight of one class, even though the first just repeats the class .gator. Because two classes has a higher weight than one, the input text will be purple! 🚀

You shouldn't ever need to use this trick in production-level code because it's an indicator that you have styles of very similar weights competing. Ideally, you should resolve those styles to be more clearly defined. However, this is a nice tool in your CSS toolbox when you're in a "just make it work" mode. ✨

The Company Analogy for CSS Specificity

Specificity can sometimes feeling counter-intuitive, especially if you’re stuck in a downward spiral of adding more and more selectors to your CSS. One way I find helpful to think about specificity is by thinking of a company with clear seniority levels.

Here are our company’s roles in order of seniority (low to high):

  • employees
  • managers
  • directors
  • VPs
  • one CEO (we’ll come back to the CEO in a bit)

Let’s map these roles to our selectors now:

  • employees: type and pseudo-element selectors
  • managers: class, attribute, and pseudo-class selectors
  • directors: ID selectors
  • VPs: inline styling

Let’s look at a new example. In this case we have a paragraph with two classes and an ID.

<p id='gatorParagraph' class='gator reptile'>
  blah blah blah
</p>
#gatorParagraph {
  color: purple;
}

p.gator.reptile {
  color: green;
}

We have one block that uses an ID only and another block that uses an element type (<p>) and two classes. At first glance it could be hard to know which one will get applied.

If we use our company analogy, we have one director saying to make the text purple. On the other hand, we have an employee and two managers saying to make it green. Because the director has the highest seniority, she can trump the employee and managers’ opinions. So the text has to be purple!

(No comment on whether this is how decisions should be made at the company 😉).

Consider each weight level a way of escalating the decision of which styles to apply. If it can’t be decided by the selectors on the same level, escalating it will give the decision to the selector of higher seniority.

As a general rule, it's best to write CSS that is specific enough you're not going to have to find ways to keep escalating it. Be aware that once you introduce a new level of seniority, like an ID, you could be overriding other styles unintentionally too.

!important is the CEO

So far we haven’t discussed who our company’s CEO is in this analogy for CSS specificity. What’s higher than inline styling? The !important rule!

Using our same example as before, let’s add !important to the block that previously had a lower weight/seniority.

#gatorParagraph {
  color: purple;
}

p.gator.reptile {
  color: green !important;
}

Adding !important will always escalate the seniority to be the highest possible level. With our company analogy, we now have a director vs. an employee, two managers, and the CEO. As soon as the CEO is introduced, she can trump anything else being suggested. 💣

Please Don’t Use !important

The !important rule is a slippery slope and seeing it in your CSS styles is generally a red flag that the specificity needs to be reorganized. It is rare that there will be an element that can only be styled by nuking the rest of your CSS rules that would otherwise be applied.

Once you introduce !important, it often leads to being overused like in the example below. The !importants cancel each other out and you’re back to having to understand the weight and cascading effect of each block.

#gatorParagraph {
  color: purple !important;
}

p.gator.reptile {
  color: green !important;
}

Instead of using the !important rule, try opening your dev tools in the browser to get a better understanding of which styles are interfering with your new ones.

Once you know what styles are causing you trouble, you can either add additional selectors of the same level of weight, or update your markup to use different selectors. 🌈

Using !important will often inevitably lead to having competing CSS styles that both use !important. 🙈

Universal Selector and Combinators

The universal selector (*) and combinators do not impact the weight of your selectors. Combinators include child selectors (>), general sibling selectors (~), and adjacent sibling selectors (+).

For example, if we have a paragraph with two spans, using a child combinator will not increase the specificity over not using it:

<p id='gatorParagraph'>
  <span class='reptile'>eggs</span>
  <span class='reptile'>nest</span>
</p>
#gatorParagraph > .reptile {
  color: purple;
}

#gatorParagraph .reptile{
  color: green;
}

These blocks of CSS has the same specificity – the child combinator makes no difference – so the text will be green.

One of the best visuals for understanding CSS specificity is called specifishity, by Estelle Weyl. Check it out! 🤓

Also, if you’re not keen on adding up specificity weights yourself, there’s a specificity calculator that will do the math for you. 🥳

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#