Unpack CSS: Cascade and Inheritance
How browser determines the final CSS rule applied to HTML elements on a web page.
In this article
Cascading Style Sheets (CSS) is a styling language that dictates how elements within an HTML document are presented visually. Any given HTML element can be styled by numerous CSS properties, sourced from different locations, and applied in various ways. The browser is responsible for resolving which CSS properties ultimately take effect. A deep understanding of this process is essential for writing efficient and well-structured CSS.
This article explores the foundational concepts of CSS and the process by which the final value of a CSS property is calculated. To illustrate these concepts, let’s begin by examining a code snippet from a quiz tweet created by Max Stoiber:
How is that possible? Let’s unpack!
Idioms
To fully grasp the concepts we’ll be discussing, it’s essential to have a solid understanding of certain CSS terminology. Let’s examine this code snippet as an example:
From the code snippet above, we can observe some terminologies:
- Selector: This is used to target specific elements within our HTML document that we want to style. In the given code,
p
and.text
are examples of selectors. - Property: This refers to the specific visual characteristics we want to modify. In this case,
color
andfont-size
are the properties being set. - Declaration: A declaration consists of a property and its corresponding value. For instance,
color: aqua
andfont-size: 16px
are CSS declarations. - Rule: A CSS rule consists of a selector followed by one or more declarations enclosed within curly braces.
- Style Sheet: A style sheet is a collection of CSS rules stored in a separate file. It’s used to define the style and layout of a web page.
- User Agent: A user agent is a software application, such as a web browser (e.g., Google Chrome, Mozilla Firefox), that interprets CSS and HTML code and renders the content on a user’s device.
The Cascading Nature of CSS
CSS allows multiple styles to be applied to a single HTML element. These styles can come from different parts of your CSS or even from the browser’s default stylesheet. When this happens, CSS uses a mechanism called the cascade to determine which style rule wins. The cascade considers factors like the specificity of the selector, the order in which the rules are defined, and the !important
assignment of certain rules. By understanding the cascade, we can control how elements are styled on a web page.
Let’s deep dive into each factors of the CSS cascade!
Order and Origin
The order of declarations within our CSS is a key factor in the cascading process. When multiple declarations apply to the same element, the declaration that appears last in the stylesheet will generally override the earlier ones. Please, check the following code snippet.
Based on the code, the final value for the font-size
property of the paragraph element will be 2rem
. The font-weight
will be set to 600
.
Furthermore, it’s important to understand that stylesheets involved in the cascading process can originate from multiple sources. There are primarily three main origins for stylesheets
-
User-Agent Origin (Browser)
This refers to stylesheets that are provided by the browser itself. These styles are applied to basic HTML elements and can vary from one browser to another. To ensure consistent styling across different browsers, you can use libraries like reset.css or normalize.css. Browser-provided stylesheets have the lowest priority in the cascade.
-
User Origin
Users can customize the appearance of web pages through their browser settings. For example, Mozilla Firefox allows users to adjust font sizes and styles. Stylesheets derived from user preferences have a higher priority than user-agent stylesheets
-
Author Origin
This refers to stylesheets that are created by web developers. These stylesheets have the highest priority and can override styles from the user-agent or user origins. This includes custom CSS rules and styles defined within HTML elements using the
style
attribute (e.g.,<div style="background: red;">...</div>
).
Additionally, there are two other types of origins: animation origin and transition origin. These are “virtual” origins that represent the effects during transitions and animations. Transition origin has a higher priority than animation origin, and both have a higher priority than the three main origins mentioned above.
Specificity
Based on the order and origin rules above, the last declaration for a property applied to an element typically takes precedence. However, there are cases where a declaration written earlier might override a later one. This occurs when the earlier declaration has a higher specificity. Specificity is a weight assigned to each CSS declaration and is a measure of how precisely a selector targets an element. If two declarations target the same element but one has a more specific selector, the more specific declaration will take precedence, regardless of its position in the stylesheet.
Browsers calculate specificity by assigning a weight to the selector of a CSS rule. To simplify, we can assign specific weights to different types of selectors:
- ID Selector: Weight of 100. Examples:
#login-form
,#main
. - Class Selector: Weight of 10. Examples:
.blue
,.red
,.header
. - Attribute Selector: Weight of 10. Examples:
[type=checkbox]
,[href*="kukuhsulistyo.com"]
. - Pseudo-class Selector: Weight of 10. Examples:
:hover
,:first-child
,:nth-child(n)
. - Type Selector: Weight of 1. Examples:
h1
,p
,form
. - Pseudo-element Selector: Weight of 1. Examples:
::after
,::before
,::first-letter
. - Universal Selector:
*
has a weight of 0. - Combinators: Weight of 0. Includes
+
,>
, and~
. - Negation:
:not(...)
has a weight of 0, but the selectors inside still have weight.
When a CSS rule targets multiple elements using a combination of selectors, the overall specificity is calculated by adding up the weights of all the selectors involved. To better understand this concept, let’s consider the following example:
Calculation: form
has weight of 1 and #login
has weight 100. The final weight is 101.
Calculation: ul
has weight of 1, >
has weight of 0, li
has weight of 1, and :first-child
has weight of 10. So, the final weight is 12.
Calculation: a
has weight 1, [href*='kukuhsulistyo.com']
has weight of 10, and .primary
has weight of 10. The final weight is 21.
Websites like specifishity.com and cssspecificity.com offer a wealth of examples and visual aids to help you grasp this concept.
!important
Declaration
A CSS declaration with !
followed by important
keyword marked it as important declaration. It means that the declaration has the highest priority and will override any other declaration, no matter how specific or where it is defined.
The use of !important
will also affect the cascade mechanism that we discussed earlier.
-
Affect on Order and Origin
The
!important
declaration overrides the typical cascading order. When an!important
declaration is present, the following priority order applies:- User-Agent origin.
- User origin.
- Author origin.
- Animation origin.
- Author origin with
!important
. - User origin with
!important
. - User-Agent with
!important
. - Transition origin.
-
Affect on Specificity
!important
declarations have the highest specificity, effectively bypassing the normal specificity calculations and ensuring that the declared style is applied. Often it represented by weight of 10,000.
While !important
declarations can be useful in certain situations, they should be used considerably. The use of !important
declarations can have unintended consequences on the cascading process, making it harder to maintain and debug your stylesheets. It’s generally recommended to rely on the natural cascading order to prioritize styles whenever possible.
Inheritance
In addition to the cascade, CSS also has inheritance concept. When a CSS property doesn’t have a value explicitly set for an element, it inherits the value from its parent element. For example, the line-height
property will inherit its value from its parent if it’s not explicitly set. Properties that can be inherited are called inherited properties. You can find this information in the MDN documentation. For instance, the color
property is marked as “inherited: yes”, meaning child elements will have the same color as their parent by default.
Let’s examine the following code snippet:
The ‘Halo-halo Bandung’ text within the h1
element will be ‘lime’ due to a specific style declaration. The ‘Song by Ismail Marzuki’ text within the p
element will inherit the color ‘navy’ from its parent, the header
, as there’s no explicit color
declaration for the p
element. If the header
doesn’t have a declared color
, the browser will continue searching up the DOM tree until it finds a color
declaration.
Unlike the color
property, the border
property is not inherited. Therefore, only the header
element will have a border. To make an element inherit a property, you can explicitly set its value to inherit
.
Summary
Alright. We’ve covered the fundamental concepts of CSS: the cascade and inheritance. Now, let’s dive into why both paragraphs in the quiz ended up being blue.
- Order and origin: Both declarations originated from the author (the developer), but
color: blue
was declared last, giving it a higher priority. - Specificity: Both declarations have the same specificity of
10
, as they both use a class selector. In this case, specificity doesn’t play a role in determining the priority. !important
declaration: Neither declaration uses!important
, so there’s no overriding rule based on that.- Inheritance: Since the color property is explicitly set for both paragraphs, inheritance doesn’t come into play.
- Order of
class
attributes: The order of class attributes in the HTML element doesn’t affect the priority of the styles.
Therefore, because the color: blue
declaration was the last one specified and there were no other factors overriding it, both paragraphs ended up with the color blue.
Yeay! We’ve unpacked the fundamental concepts of CSS. These concepts are not designed to make our lives difficult as developers, but rather to provide a structured, maintainable, and scalable way to style our web pages. To effectively use CSS, it’s essential to understand these concepts.
For a more in-depth understanding, I recommend reading the official W3C specification on the CSS Cascade and Inheritance. Additionally, for those interested in CSS architectures that leverage the cascade and inheritance effectively, I suggest exploring these methodologies: BEM, ITCSS, SMACSS, and CUBE.
I hope this unpacking has shed some light on the topic of CSS. Thank you for taking the time to read this!