What Does “Specificity” Mean in CSS?

CSS uses a numeric scale to determine the power of a selector. Power, as in which one is the strongest, or in other words, which selector has the highest specificity. The selector with the highest specificity number will always dictate how an HTML element looks, no matter which styles were applied to it by other, weaker selectors.

If you have a button element, there are many ways to apply styles to it. In CSS the 3 commonly used selectors are type, class, id.

You can style an element directly via the type selector:

button {
  background-color: red;
}

You can use a class attribute:

.button {
  background-color: red;
}

Or an id attribute:

#button {
  background-color: red;
}

Each selector has a numeric value of 1.

So if all CSS selectors above (type, class, id) have the same value (1), which of the rule-sets above wins, when used in the same style sheet?

ID wins (#button) because IDs take precedence over class and type selectors.

What if there is no ID involved?

Then the class selector (.button) wins because classes take precedence over type selectors.

Wait, what about CSS’s “Cascade” (CSS = Cascading Style Sheets)?

In CSS, if all things are equal, whichever selector is applied last in your Style Sheet, wins.

Let’s say you add 3 classes, with the same name (.hero-button,) in your stylesheet. Each class targets the same styling property (background-color), but each class has different property values, in this case, green, blue, red, respectively.

.hero-button {
	background-color: green;
}

.hero-button {
		background-color: blue;
}

.hero-button {
	background-color: red;
}

Then you apply all of 3 of them to the same HTML element:

<button class="hero-button">Button</button>

The last .hero-button class in the stylesheet will precede over any previous classes with the same class name, making your button’s background color red. That’s how the CSS cascade works, the order of specificity is from top to bottom, with the bottom selector winning. Remember, this is only 100% true when all things are equal.

Important: if you have multiple selectors with the same name, the one at the bottom of your stylesheet won’t override all the styles applied by a previous selector with the same name, it will only override the specified styling property.

Confused?

Don’t worry, it’ll make sense in a moment.

Here are the 3 .hero-button classes from before, all with the same background-color properties from before, but with the first two classes each getting one additional styling property (padding & font-size):

.hero-button {
  background-color: green;
  padding: 2rem;
}

.hero-button {
  background-color: blue;
  font-size: 2rem;
}

.hero-button {
  background-color: red;
}

Our stylesheet now has 3 CSS classes, with the same name, all targetting the same background-color property to the HTML button element — but the first two classes have additional styling properties. What do you think happens?

Here’s the result:

https://codepen.io/StrengthandFreedom/pen/mdyMGPL

As you can see, our button element stacks together (combines) all the styling properties from our 3 .hero-button classes, and now our button element gets all 3 properties:

  padding: 2rem;
  font-size: 2rem;
  background-color: red;

In other words, when you have CSS selectors in your stylesheet with identical names, you will only get properties overridden if your selectors target the same properties.

In the example above, all 3 classes target the background-color property, and here the bottom class wins, because of the CSS Cascade rule (Order Specificity). But since the last .hero-button class in your stylesheet target, neither the padding nor the font-size property, those properties from the first two .hero-button classes is allowed to be applied to the button element.

The reason I beat the dead horse on this point is that you will inevitably find your self in a situation where you’re dealing with a massive, disorganized CSS codebase, where multiple developers contributed to making your life miserable.

One of the most common scenarios is that different developers have added CSS classes to the stylesheet that already exist somewhere else, and that will quickly cause confusion if you don’t know or forgot that CSS will stack properties together as we saw earlier.

So you might add a CSS class called: primary-button with the following properties:

.primary-button {
  background-color: red
  padding: 1rem;
  font-size: 1.125rem;
}

Yet for some reason, your button appears to have a shadow on it, that you didn’t add. Well, that could be caused by a couple of things, either because someone has applied styling directly on the button element:

button {
 box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
}

Or because someone has previously added a class in the stylesheet with the same name, and added the box-shadow property, like this:

.primary-button {
 box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
}

To remove the unwanted styling, you either have to locate the place in your codebase where this unwanted property is applied and remove it or directly target and remove it in your .primary-button class, like this:

.primary-button {
 background-color: red
 padding: 1rem;
 font-size: 1.125rem;
 box-shadow: 0; /*removes existing box shadow value*/
}

I know what you’re thinking. This is a messy way of solving a problem. Correct. But sometimes you’ll be exposed to a massive and disorganized project (typically with lots of legacy code), where you don’t have access to all the existing CSS code (any developer with some experience can relate to this). In this type of situation, you can’t just modify existing code as you desire — you have to override existing code, and in that case, it’s good to know how CSS Specificity and Source Order works.

You can use the Specificity Calculator on this website to compare the specificity number of different CSS selectors, including group selectors (combining selectors).

Tip: a good way to avoid dealing with specificity issues in CSS is to use primarily classes to style your UI instead of type selectors, and only using IDs sparingly. Also, avoid using group selectors if you can since they can lead to unpredictable problems in a growing codebase.