The CSS Cascade, Inheritance, and Specificity

The CSS Cascade, Inheritance, and Specificity

As a beginner diving into the world of web development, one of the fundamental technologies you'll encounter is Cascading Style Sheets (CSS). CSS is the magic behind the beautiful and responsive designs that make websites visually appealing. To become a proficient web developer, it's crucial to understand three key concepts: the CSS cascade, inheritance, and specificity.

The CSS Cascade

The CSS cascade is the process by which the browser determines which CSS rule will be applied to an element. The cascade works by assigning a weight to each CSS rule and then comparing the weight of each rule to determine which rule should be applied.

These CSS rules can come from various sources which include:

  • User Agent Stylesheets: These are the default styles applied by web browsers to HTML elements. Different browsers apply different styles depending on the element. For example, each browser styles a range input elements differently.

      <input id="name" type="range" />
    

    Brave default style for range input

    Firefox default style for range input

    Chrome default style for range input

    Microsoft Edge default style for range input

  • Author Stylesheets: These are the styles you, the developer, write in your CSS file(s). You define the colors, spacing, and so on. This allows your websites to have consistent design/views across all browsers.

  • User Stylesheets: These are the styles users apply to websites, which overrides the author's styles. However, this is uncommon among regular web users. Some frontend developers apply these stylesheets either during or after development to see what results could have been achieved.

  • Inline Styles: These are styles applied using the **style** attribute. Inline styles take precedence over external stylesheets.

      <input id="name" type="range" style="display: none" />
    

The weight of CSS rules is determined by the following factors:

  • Importance: CSS provides the !important keyword to mark certain rules as more important than others. However, it is recommended not to use the !important keyword as it makes managing your styles more difficult. For example, the heading below will have a red color.

      h1 {
          color: red !important;
      }
    
      h1 {
          color: green;
      }
    

    Red importance

  • Specificity: Specificity is the algorithm CSS use to determine which rule/property should be applied to an element. More specific selectors take precedence over less specific ones. In short, the more specific a CSS rule is, the higher its weight will be. We'll delve deeper into specificity later.

  • Source order: If two CSS rules have the same importance and specificity, the rule that appears later in the CSS file will be applied. For example, the previous heading will have a green color cause it comes later in the source order.

      h1 {
          color: red;
      }
    
      h1 {
          color: green;
      }
    

    Green importance

Inheritance

Inheritance is a process that allows certain CSS properties to be automatically passed down from a parent element to its child element(s). Think of inheritance as a family tree, where grandparents pass some traits to parents, and parents pass some traits to children.

However, not all traits from parents are passed to their children. Similarly, not all properties are passed down from parent to child elements. Text properties like color, font, line-height, opacity are inheritable, while other properties like borders, padding, margin are not inheritable.

Here is an example that demonstrates how inheritance works:

<body>
    <h1>Inheritance</h1>
</body>
body {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    color: coral;
    font-family: 'Courier New', Courier, monospace;
    border: 1px solid green;
}

Coral colored text surrounded with a green border

In the above example, notice that the h1 inherits the color and font-family properties from its parent, body, but it doesn’t inherit other properties. Specifying the same font-family on the h1 isn’t necessary due to inheritance. Knowing which properties inherit and which don't can save you from unnecessary duplication of style declarations.

h1 {
    font-family: 'Courier New', Courier, monospace; // not necessary
}

While some properties are inherited by default, you can use the inherit keyword to explicitly inherit any parent property. From the previous example, if we want the <h1> to inherit the parent's border, we can do so like this:

h1 {
    border: inherit;
}

Coral colored text surrounded with double green borders

Specificity

Specificity is a method used to determine which CSS rule should be applied to an element in situations where conflicting rules exist. It involves assigning a weight to different CSS rules to determine which one is the most specific and therefore takes precedence.

Specificity is calculated using various selectors, which are organized in order of decreasing specificity:

  • id selector: Styling by id is more specific than any other selector. Think of id as the king of a village. You can only have a king in a village, which means you can only have a unique id.

      #myId {
          color: red;
      }
    
  • class selector: This selector includes classes like .myClass, attributes like [type="search"], and pseudo-classes like :hover. No matter the number of class selectors combined, they will never override an id selector. Think of them as the queens within a village.

      .myClass {
          color: brown;
      }
    
  • Type selector: This selector includes HTML tags like div, table, section and pseudo-elements like ::before and ::placeholder. Think of them as the heirs of a village.

      section {
          color: yellow;
      }
    
  • Universal selector: This selector matches all elements of any type. It is denoted by *. It does not impact the specificity value in any way.

      * {
          color: coral
      }
    

    In the absence of all selectors, all elements on a page will have a coral color. The universal selector is commonly used to reset the padding, margin and box-sizing of all elements on a page.

      * {
          padding: 0;
          margin: 0;
          box-sizing: border-box;
      }
    

Combining Selectors and Calculating Specificity

You might be wondering, what happens when these selectors are combined? When selectors are combined, their specificity values are also combined. Specificity is usually represented as a three-component value: [a, b, c]. These components represent the specificity of ID selectors, class selectors, and type selectors, respectively.

For example, let's say we have the following selectors on this element:

<section id="myId" class="myClass anotherClass">
    Hello
</section>
#myId.myClass {
    color: purple;
}

section#myId.myClass {
    color: orange;
}

#myId.myClass.anotherClass {
    color: orange;
}

section#myId.myClass.anotherClass {
    color: brown;
}

section.myClass.anotherClass {
    color: orange;
}

section {
    color: blue;
}

In this scenario, each specificity here is:

  • myId.myClass: [1, 1, 0]

  • section#myId.myClass: [1, 1, 1]

  • #myId.myClass.anotherClass: [1, 2, 0]

  • section#myId.myClass.anotherClass: [1, 2, 1]

  • section.myClass.anotherClass: [0, 2, 1]

  • section: [0, 0, 1]

We will follow the following process to determine which specificity prevails:

  1. First, eliminate any of the specificity without an id. This leaves us with myId.myClass, section#myId.myClass, #myId.myClass.anotherClass and section#myId.myClass.anotherClass.

  2. Then pick the ones with the highest number of class selectors. This leaves us with #myId.myClass.anotherClass and section#myId.myClass.anotherClass. Notice that even though section#myId.myClass has another selector, we discarded it because a class selector is always greater than type selector no matter the number of type selectors.

  3. Lastly, pick the one with the highest number of type selectors. This leaves us with section#myId.myClass.anotherClass which means that section will have a brown color.

Brown colored text

Combining the three: The CSS Cascade, Inheritance and Specificity

To combine these three concepts, consider the following example:

<section id="parent">
    <header class="child">
        <h1 class="grandchild">Hello, World!</h1>
    </div>
</div>
#parent {
    background-color: red;
}

.child {
    background-color: blue;
}

.grandchild {
    color: white;
}

The CSS rules above will apply a red background color to the parent section, a blue background color to the child header, and a white color to the text of the grandchild h1.

If you change the color of the grandchild to black, it will override the parent and child rules because it has a higher specificity. Similarly, if you change the background color of the child to green, it will override the parent rule because it has the same specificity but comes later in the cascade.

#parent {
    background-color: red;
}

.child {
    background-color: blue;
}

.grandchild {
    color: white;
}

.child {
    background-color: green;
}

Finally, if you add an !important keyword to the child's background color, it will override all other rules regardless of their specificity or position in the cascade.

#parent {
    background-color: red;
}

.child {
    background-color: blue;
}

.grandchild {
    color: white;
}

.child {
    background-color: green;
}

header {
    background-color: aqua !important;
}

It's important to keep these concepts in mind when writing CSS to ensure that your styles are applied as intended and that you can easily manage and maintain your codebase.

Conclusion

To summarize, understanding the CSS cascade, inheritance, and specificity is crucial to becoming a CSS master.

  • The cascade determines which CSS rule will be applied to an element.

  • Inheritance allows certain CSS properties to be passed down from a parent element to its child element(s).

  • Specificity determines which CSS rule should be applied to an element in situations where conflicting rules exist.

  • !important should be a last resort.

  • Avoid combining multiple selectors. e.g section#myId.myClass.anotherClass

  • Avoid id selectors, instead use other selectors like class.

By mastering these concepts, you can write efficient and maintainable CSS code for your web projects.