In this article, we’re going to review some of the most fundamental concepts of CSS. The cascade, inheritance and specificity.

What is the Cascade?

It’s the ‘C’ in CSS (Cascading Style Sheets), so conceptually it lies at the very core of CSS. The cascade is the algorithm which determines the properties applied to page elements.

This determination is made based on inheritance, specificity, importance, & rule order.

The cascade also ensures we have no conflicts in our stylesheets. If we have multiple CSS rules, for the same property & on the same element - it determines which rule is applied.

So let's keep the cascade in mind, while we take a look at the rules it uses to make these decisions!

Inheritance

Inheritance works on a property by property basis. When you set properties on a selector in CSS, they're either inherited by all the children of that selector or they're not. It depends on the property!

The reason being that some properties make sense to be inheritable, whilst others do not.

When working with fonts, you don’t need to apply styles such as font-family or font-size to every single element on your page. You can simply set these styles on the body tag & every child will inherit it.

However a property such as background-color, makes little sense to be inherited. So the children of the element that this is applied to, will not inherit this property.

You can check if a particular property is inheritable here.

Inheritance helps us write our CSS much more concisely. As we don't have to explicitly set every property, in every child element!

Enforcing Property Inheritance

If a property is not inheritable by default, you can force inheritance to its children.

In the child selector, you set the property value to inherit.

body {
   background-color: red;
}

p {
   background-color: inherit;
}

Avoiding Property Inheritance

The reverse is also possible. By using the revert keyword, the property will not inherit from its parent. In this case, the value is reverted to the original default given by the browser.

Initial & Unset

It’s worth mentioning that we can also use the initial & unset keywords.

  • initial: sets a CSS property to its default value (the browser default).
  • unset: resets a property to its inherited value if it inherits from its parent, and to its initial value if not.

Specificity

When an element is targeted by multiple CSS rules — specificity comes into play!

Let’s take a look at the following element:

<p class="surname">
  Smith
</p>

We could have one rule selecting our class:

.surname {
  color: red;
}

And another rule targeting p, which sets the color to a different value:

p {
  color: green;
}

And we could even have another rule targeting p.surname!

So which rule will take precedence over the others, and why?

This is where the rules of specificity come into consideration. And the more specific rule will always win. If we have two or more rules with the same specificity, the one that appears last wins.

Specificity is determined by a calculation. The more specific a style rule is, the higher point value it accrues, and the likelier it is to be present on the element’s style.

How we calculate specificity

Understanding how specificity works, will greatly reduce any frustrations we may have with CSS.

If we’ve applied a style to an element that isn’t taking effect, the culprit is likely a selector that isn’t specific enough!

To perform the calculation, think of 4 slots with each of them starting at 0.

0 0 0 0

The slot on the left is the most important, and the rightmost is the least important.

Therefore 1 0 0 0 is higher than 0 1 0 0.

Slot 1

The first slot, the rightmost one (0 0 0 ``0), is the least important.

This value changes when we have a type selector. A type is a tag name. If you have more than one type selector in the rule, increment the value stored in this slot.

For example:

p {} 		 /* 0 0 0 1 */
span {} 	 /* 0 0 0 1 */
p span {} 	 /* 0 0 0 2 */
p > span {}      /* 0 0 0 2 */
div p > span {}  /* 0 0 0 3 */

Slot 2

The number in the second slot is determined by the following:

  • Class selectors
  • Pseudo-class selectors
  • Attribute selectors

Every time a rule contains one of the above selectors, we increment the value of the second column from the right.

For example:

.city {}	    /* 0 0 1 0 */
.users .city {}	    /* 0 0 2 0 */
[type="button"] {}  /* 0 0 1 0 */
:hover {}	    /* 0 0 1 0 */

And naturally, slot 2 selectors can be combined with slot 1 selectors:

p .city {}	         /* 0 0 1 1 */
input[type="button"] {}  /* 0 0 1 1 */
.images img:hover {}     /* 0 0 2 1 */

Slot 3

Slot 3 is reserved for id’s.

Every HTML element can have an id attribute assigned. And we can use id’s to target elements specifically.

For example:

#city {}	/* 0 1 0 0 */
.user #city {}	/* 0 1 1 0 */
#city span {}	/* 0 1 0 1 */

Slot 4

Slot 4 is altered by inline styles. Any inline style will have precedence over any rule defined in an external CSS file, or inside the style tag in the page header.

For example:

<p style="color: yellow">Text</p>  /* 1 0 0 0 */

Even if any other rule in the CSS defines the color, the inline style rule will be applied.

There is just one exception— if !important is used.

Importance

Our specificity is completely void if a rule ends with !important:

p {
  font-size: 18px !important;
}

By adding !important to a CSS rule, it’ll be made more important than any other rule, according to the specificity rules.

Importance seems like a magic way to ensure a style is applied, however, it can become a nightmare to work with down the line. As the only way you can override !important is with another !important. Thus you can really make a mess of your CSS when you go down that path.

Best practice is to not use importance. Instead you should look for a more specific selector.

Summing up..

A great rule of thumb is to only use the amount of specificity you need, but no more. So you can craft selectors that overwrite rules set by preceding rules, without too much difficulty!

Related Posts: