Illustrated CSS: CSS Cascading and Inheritance

w3cplus
24 min readJan 8, 2024

There are three concepts in CSS that must be mastered: Cascading , Inheritance and Weight . Today we will mainly learn about Cascading and Inheritance in CSS. The CSS Weight part will be introduced in the CSS selector, because this part is more closely coupled with the CSS selector. Whether you are a beginner or a colleague with some work experience, it is necessary to spend some time reading this article, which will help you have a clearer understanding and comprehension of CSS.

In the eyes of many web developers, CSS is not a programming language, but it is truly a computer language . It is mainly used to add styles to structured documents such as HTML, XML, etc. It is mainly defined and maintained by W3C. CSS is an abbreviation of the three words Cascading Style Sheets, which many people call Cascading Style Sheets or Cascading Style Sheets . The first concept we will talk about next is Cascading in CSS, which corresponds to the first letter C in CSS. By now, you may know why Cascading is one of the important concepts in CSS.

Stacked

Cascading is an inherent feature of CSS, which endows Cascading style sheets, also known as cascading Cascading is a powerful tool. If used incorrectly, it can lead to the fragility of style sheets, causing headaches for web developers when they have to make changes at any time. For example, colleagues who are new to CSS, especially programmers from other parts of the web (such as server level and client), often encounter a phenomenon when writing CSS independently: “ Why doesn’t the style work? “ Faced with such a scenario, many people will use very violent means to deal with it, such as adding ! important or directly adding internal connection CSS to HTML elements. This is a point that many colleagues are confused about. Why do we need to do this? In fact, this is closely related to the cascading (also known as cascading ) that will be introduced next.

Cascade definition

Since we will delve into the details of how CSS stacking works, it is important to understand how the W3C specification defines it:

The cascade takes a unordered list of declared values for a given property on a given element, sorts them by their declaration’s precedence, and outputs a single cascaded value.

Roughly speaking, the stack will take a bulleted list of declared values for the given attribute on the given element, sort them by declared priority, and output a single stack value .

CSS Cascading is an algorithm that browsers use to decide which CSS style rules to apply to an element. Many people like to think of it as the “winning” style, which has higher weight in CSS terminology (we will go into more detail later).

To better understand CSS stacking, consider CSS declarations as specific “properties”. These properties can be various parts of the declaration, such as CSS selectors or CSS properties, or even position-related CSS declarations (such as their original or source code location).

CSS Cascade will take some of these properties according to its own algorithm and assign a weight to each property. If the CSS rule wins in high priority (selector weight is high), then the style rule will win and take effect. However, if two rules conflict under the given weight, the algorithm will continue to “ cascade down “ and check the low-priority properties until a winning rule is found.

Simply put, when multiple conflicting CSS declarations are applied to the same element, the CSS stacking algorithm will list them in order from the highest weight to the lowest weight according to a certain mechanism.

  • Sources and importance
  • Selector weight
  • Order of appearance
  • Initial and inherited properties (default values)

Next, we will focus on these points.

Sources and importance

The highest weighted attribute of cascade checking is the combination of the importance and source of the given rule. As for the source of CSS rules, the rules mainly come from three places.

  • Author Rule : This is the CSS declared by the HTML document. It is written by the front-end developer to specify style sheets to the source document according to the document language (such as HTML) convention. This is also the only source we can control
  • User (User) : This is defined and controlled by the user of the browser. Not everyone will have one, but when people add one, it is usually to override styles and increase website accessibility. For example, the user can specify a file with a style sheet, or the user agent may provide an interface to generate user styles (or behave like this)
  • User-Agent : These are the default styles provided by browsers for elements. This is why input looks slightly different on different browsers, and this is one of the reasons why people like to use CSS to reset styles to ensure that user agent styles are rewritten

Note that users may modify system settings (such as system color scheme), which will affect the default style sheet. However, some user agent implementations make the values in the default style sheet immutable

These three style sheets will overlap to a certain extent, and they will affect each other cascading.

CSS cascading gives weight to each style rule, and when applying these rules, the rule with the highest weight takes precedence. By default, rules in the author’s style sheet have higher weight than rules in the user’s style sheet. The importance of CSS declarations is determined by the appropriately named ! important syntax. ! important CSS rules automatically jump to the front of the cascading algorithm, which is one of the reasons why the use of ! important in styles is not encouraged. Styles that use ! important can only be completed using other ! important rules. If your project is large enough and uses ! important enough, your CSS will become more fragile and difficult to maintain. For the use of ! important , it is recommended that you only use it when all other methods are invalid.

So how does the cascading algorithm in CSS determine which declaration wins?

CSS cascading considers the combination of these two properties when determining which declaration wins. Each combination has a weight (similar to the partial weight of the declaration), and the rule with the highest weight wins. The following are combinations of various sources and importance considered by browsers, listed in order from highest to lowest weight.

  • Band ! important user agent rules
  • Band ! important user rules
  • Band ! important writer rules
  • Rules in CSS animations @keyframes (this is a special part, it still comes from the author rules, but because animations are temporary or short-lived, browsers give them slightly more weight than normal author rules)
  • Writer rules
  • User rules
  • User agent rules

When the browser encounters two or more conflicting CSS declarations, and one of them wins at the source and importance levels, CSS stacking will resolve this rule. However, if the conflicting declarations have the same importance and importance level, the stacking will continue to consider the weight of the selector.

Selector weight

The second weight in CSS cascading is the weight of the selector . In this layer, the browser looks at the selectors used in the CSS declaration. As front-end developers, we can only control the author style rules. Because we can’t make too many changes to the rules of the source. However, you will find that as long as you don’t use ! important in the code, there are still many ways to control the selector weight layer in CSS cascading.

The weight of a selector will be calculated according to the following rules:

  • If the declaration comes from an inline style ( style attribute) instead of a selector style rule, it counts as 1 , otherwise it is 0 ( = a ). In HTML, the style attribute value of an element is a style rule, and these attributes have no selectors, so a = 1, b = 0, c = 0, d = 0 , that is 1, 0, 0, 0
  • Count the number of ID attributes in the selector ( = b )
  • Count the number of other attributes and pseudo-classes in the selector ( = c )
  • Count the number of element names and pseudo-elements in the selector ( = d )

Four numbers joined together a-b-c-d (in a large cardinal number system) represent specificity, as in this example:

{}                   /* a=0 b=0 c=0 d=0 -> 选择器权重 = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> 选择器权重 = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> 选择器权重 = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> 选择器权重 = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> 选择器权重 = 0,0,0,3 */
h1 + [rel=up]{} /* a=0 b=0 c=1 d=1 -> 选择器权重 = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> 选择器权重 = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> 选择器权重 = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> 选择器权重 = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> 选择器权重 = 1,0,0,0 *

There are also many vivid diagrams on the Internet to explain the CSS selector weights, such as the following figure:

The above image is from @Estelle Weyl’s article “ CSS Specificity “.

Order of appearance

The last major layer of CSS stacking algorithm is calculated in the order it appears in the source code. When two selectors have the same weight, the last declaration in the source code wins.

Because CSS considers source order in cascading, the order in which style sheets are loaded is particularly important. If two style sheets are introduced in the < head > of an HTML document, the second style sheet will override the rules in the first style sheet.

Initial and inherited properties

Although the initial and inherited values are not really part of the CSS cascade, they determine what happens if there is no CSS declaration for the element. In this way, they determine the default style value of the element.

A more detailed introduction to initialization and inheritance will be elaborated in the next section. Interested colleagues, please continue reading.

CSS entities involved in cascading calculations

Only CSS declarations, which are property name-value pairs, participate in cascading calculation. This means that @ rules containing entities other than CSS declarations do not participate in cascading calculation, such as @font-face containing descriptors. For these cases, @ rules participate in cascading calculation as a whole, such as the cascading of @font-face is determined by its descriptor font-family . If the same descriptor is defined multiple times @font-face , the most suitable @font-face is considered as a whole.

CSS declarations contained in most @ rules participate in the cascading calculation, such as those contained in @media , @documents , or @supports , but declarations contained in @keyframes do not participate in the calculation, just as @font-face participates in the filtering of the cascading algorithm as a whole.

Note that @import and @charset follow specific algorithms and are not affected by cascading algorithms .

CSS @layer rules

To solve such problems (cascading and weights), modern CSS adds a cascading layer rule, @layer .

Simply put: Cascading layers provide a structured way to organize and balance CSS rules in a single source that ultimately determines who wins !.

Due to the unique position of the cascading layer in CSS cascading, using it has some benefits, allowing developers to have more control over the cascading. The cascading layer of CSS is generally located between the “Style Attribute” and the CSS selector weight (Specificity), namely:

I use the following diagram to illustrate the differences in CSS cascading before and after @layer :

The direct change brought about is that the weight calculation rules have changed.

With the CSS cascading layer @layer feature, you can abandon some of the previous CSS methodologies (such as ITCSS), because @layer can better help you manage CSS cascades.

Simply put, cascading layers @layer is a new feature of CSS that affects the application and priority of style rules. Here are some key details about cascading layers:

  • Add to Cascade Layer: Styles can be added to a specified cascade layer using the @layer rule. This allows developers to organize styles more orderly.
  • Anonymous Cascade Layer: If the @layer rule is not used, the style will be placed into the default anonymous cascade layer. This ensures that styles that do not specify a cascade layer will not affect the styles of the specified cascade layer.
  • Predefined cascading layer order: Cascading layers can predefine an order to ensure that styles are applied according to the default predefined cascading layer order when cascading layers are not explicitly specified in style rules.
  • Load external CSS files to the cascade layer: You can load external CSS files into the specified cascade layer through the @layer rule, which can organize and load styles more flexibly.
  • Cascade-less styles: You can use the @layer unlayered rule to add styles to the style layer of a cascade-less layer, and such styles are not inherited by any cascade layer by default.
  • Cascade layer nesting: You can nest one cascade layer within another to create a more complex style hierarchy.
  • Rollback Cascade Layer: You can use the @layer rule to roll back to the previous cascade layer state to undo certain style changes.

Cascading layers provide developers with the ability to manage and organize styles more finely, making it easier to maintain and extend styles in large projects.

For a more detailed introduction to the CSS cascading layer @layer feature, please read the CSS Layering: @layer course in Modern CSS !

Inheritance

In CSS, the overview of each CSS property definition indicates whether the property is inherited by default or not. This determines how to calculate the value when you do not specify a value for an element’s property. When an element’s inherited property does not specify a value, the calculated value of the same property of the parent element is taken. Only the document root element takes the initial value given in the overview of the property; when a non-inherited property of an element does not specify a value, the initial value of the property is taken. For example:

html {
font-size: small;
}

This rule sets a font-size attribute on the root element of the HTML document html , and this attribute is inherited (some attributes are naturally inherited in CSS). As shown above, all descendant elements of the html element will inherit this attribute, as shown in the blue box in the figure below:

The blue box in the above figure tells us that the body element inherits from the html element's font-size: small; . The developer tools will prompt us with " Inherited from html ". So the question is, which properties in CSS will be inherited? In fact, the description of each property in the W3C specification has already told us clearly. For example, the border property, when describing its syntax, has a Inherited description item with a value of no in the list. This also tells us that the border property cannot be inherited. Conversely, looking at the font-size property, the list of syntax descriptions also has a Inherited description item, but its value is not no , but yes , which means that the font-size property will be inherited.

This determines how to calculate the value when you do not specify a value for an element’s attribute.

If you usually read the specification carefully, it is not difficult to find that when introducing the syntax parameters of each property, there will be an Initial parameter, which mainly specifies the initial value of each property. The initial value given by CSS properties has different meanings for different inherited and non-inherited properties:

  • For inherited properties, the initial value can only be used on root elements without a specified value
  • For non-inherited properties, the initial value can be used on any element that is not specified

This introduces two concepts: initial value and inherited value . In addition to these two concepts, there is also a calculated value in the CSS property, which is calculated from the specified value:

  • Handling special values inherit and initial
  • Calculate the value according to the method described in the summary of the attribute

Calculated values for computed properties often involve converting relative values to absolute values, such as units like em and % . For example:

font-size: 16px;
padding-top: 2em;

Where padding-top: 2em is a computed value, the computed value will be calculated based on the font-size as the base (in this example), and the computed value here is 32px . However, some attribute percentage values will be converted to percentage calculated values (such as width ). In addition, the line-height attribute value is a number without units, and its value is also a computed value.

The calculated values of CSS may sometimes have slight deviations in different browsers.

If you are interested, you can open the browser’s developer tools to view the corresponding calculation value (for example, Chrome developer tools, there is a Computed option , which displays the corresponding CSS calculation value), as shown in the figure below:

The most important use of calculated values is inheritance, including the inherit keyword.

Finally, two points to summarize:

  • When an inherited attribute of an element does not specify a value, the calculated value of the same attribute of the parent element is taken, and only the document root element takes the initial value given in the probability of the attribute
  • When a non-inherited attribute of an element does not specify a value, the initial value of the attribute is taken

By now, you may know what inheritance and non-inheritance mean, as well as their value-taking methods. But you may still be struggling with which properties in CSS are inherited and which are not. Actually, I can’t answer this question accurately because I haven’t done any statistics on this. However, I can share two small experiences with you.

  • In CSS, some properties such as fonts, text, and colors are inheritable properties
  • In CSS, some properties related to layout and box models are non-inherited properties

If you want to know the answer accurately, you can check the property table organized here . As long as the Inherited option is Yes , it means it is an inherited property, otherwise it is a non-inherited property.

Mechanism for handling CSS inheritance

CSS provides a mechanism for handling CSS inheritance. Simply put, CSS provides several property values that can be used to handle property inheritance. These property values are initial , inherit , unset , and revert . In fact, in addition to these four property values, there is also a all property value. Although these property values are mainly used to help you handle CSS property inheritance, there are still some differences in their use. Let's use a picture to illustrate the differences between them:

Next, let’s take a look at the actual usage and corresponding differentiation of these attribute values.

initial

In CSS, each property has an initial value, which is actually the default value of the CSS property. The CSS specification defines the initial value of each property. For example, the initial value of text-align is left , and the initial value of display is inline .

And here, we want to talk about the CSS keyword initial :

If the cascaded value is the initial keyword, the property’s initial value becomes its specified value.

The general meaning is: “If the cascading value is the initial keyword, the initial value of the attribute will become its specified value." In other words, if you explicitly set the value of a property to initial in the element style settings, it actually means that the default value of the property has been set.

It may be a bit confusing to understand from the text. Let’s use a small example to help you understand. Assuming we have a < p > element, colleagues who have come into contact with CSS know that it is a block element. To make it look good, let's add some decorative style code:

p {
background: #f36;
padding: 2rem;
font-size: 2rem;
color: #fff;
}

The effect looks like this:

If we want the p element to become an inline element, as we did before, we need to manually handle the browser default style (User-Agent style), which is the display reset:

p{
dispaly: inline;
}

The block and inline effects are as follows:

As mentioned earlier, inline is the initial value of display (i.e. default value), and it is also mentioned in the specification:

When you set the value of a property to initial in the element style settings, you actually set the default value of the property .

In other words, we can set the initial keyword for display :

p {
display: initial;
}

The effect obtained at this time is actually the same as using display: inline :

As we can see from the Computed item in the browser inspector, when the display is set to initial , the user agent's style value block is overwritten:

Next, let’s look at an inherited property color . So the descendant elements of the < p > element < strong > will also inherit the color: #fff value set in the < p > element. If we explicitly set the value of color to initial in strong , then the color of strong will be reset to the default value. Since we did not set the default color color, the browser will assign a calculated value to the initial value of color :

inherit

CSS adds an inherit keyword property value, which can force an element to inherit the value of a property of its parent element. As mentioned earlier, some properties in CSS are automatically inheritable, such as font-size , color , etc., but there are also many non-inheritable properties, such as border , border-radius , etc. Here, if the inherit keyword is displayed on a very inherited property, it means that the element will inherit the property value or calculated value specified by the parent element.

In order to better understand inherit , let's look at an example. In this example, we use border as an example:

<div class="wrapper" style="border:5px solid blue;">
<div>...</div>
</div>

As can be seen from the effect, the border style on div.wrapper does not inherit from its child element div . If we want .wrapper 's child element div to also have the border style of .wrapper element, we need to explicitly set the same border style on the element div :

<div class="wrapper" style="border:5px solid blue;">
<div style="border:5px solid blue;">...</div>
</div>

With the inherit keyword, things become much simpler, just set border: inherit; on the child elements of .wrapper :

<div class="wrapper" style="border:5px solid blue;">
<div style="border: inherit;">...</div>
</div>

The resulting effect will be the same.

The above example is that the parent element sets the border style, so it inherits the border style of the parent element. If the above example is slightly modified, a div is wrapped around the element, and this div does not do any style settings. What will it become? Let's take an example directly:

<div class="wrapper" style="border:5px solid blue;">
<div>
<div style="border: inherit;" class="ele">...</div>
</div>
</div>

The effect is as follows:

It can be seen that div.ele only inherits the calculated value of the border attribute of its parent element div , and does not inherit the setting value of the border attribute of its ancestor element .wrapper . Through the browser developer tools, it can be seen at a glance:

This example shows that even if the element itself explicitly sets the inherit keyword, if its parent element does not explicitly specify a style, its final effect will be consistent with the effect of revert . That is, it inherits the calculated value of its parent element, which is the browser default style (User Agent Stylesheet) .

revert

Revert values were previously called default . Indicates that no property values are used.

We all know that if we don’t use author style sheets (i.e. style sheets written by web developers themselves), the browser will follow this process to detect the style of element invocation:

  • The browser first checks if the element has attributes that use the author’s style sheet
  • If not found, the Client default style will be checked
  • If the Client style is not found, it is equivalent to the element applying unset

Let’s take an example. For example, if we have a div element, we don't explicitly set the value of its display property in our own style sheet. For the final rendering result, the browser will use the style of the user agent rule display: block .

As mentioned earlier, even if we explicitly set display: revert in the div , the element will still use the display: block style in the user agent rules. Similarly, we use display: initial in another div element. As mentioned earlier, this div will use the initial value of display , inline . For example, the following effect:

Let’s take a look at the application scenario of inheriting properties. Because the color: #fff; style is set on the div element, which is written by the user, and color is an inherited property, as long as it is a descendant element of the div , it will inherit the property value of color . According to the detection mechanism of revert mentioned earlier, it first detects the style written by the user, and then detects the user agent style. If neither is available, the unset style will be set. So the final effect we see is as follows: (the second one sets color: initial; ).

unset

Unset is a combination of initial and inherit . When the property is set to unset , if it is an inherited property, it is equivalent to inherit ; if it is not, it is equivalent to initial .

Some properties, if not explicitly specified, will default to inherit . For example, if we set a color to an element, it will apply to all default child elements. Other properties, such as border , are non-inherited by default.

<div class="wrapper" style="border: 5px solid blue;color: #fff;">
<div class="ele">...</div>
</div>

The effect is as follows:

In the example, the color property is inherited, but the border property is not. Adjust the above example code slightly:

<div class="wrapper" style="border: 5px solid blue;color: #fff;">
<div class="ele" style="border: unset; color:unset;">...</div>
</div>

The div.ele element's border and color are both set to unset . That is to say, it will use the value of initial or inherit . Depending on which value, it depends on what the default behavior of the property is. If the default property is inherit , inherit will be used; if the default property is initial , initial will be used.

In the example above, the border property uses initial and the color property uses inherit .

all

In CSS, all is a shorthand property that resets all properties except unicode-bidi and direction to their initial or inherited values. All has three values:

  • Initial : This keyword represents changing all attributes of the element or its parent element to the initial value.
  • Inherit : This keyword represents changing the values of all attributes of the element or its parent element to the values of their parent element attributes.
  • Unset : This keyword represents changing the value of all attributes of the element or its parent element to their parent element's attribute value if the value of the element's attribute is inheritable, otherwise changing it to the initial value.

Let’s look at an example. For example, let’s have an HTML structure like this:

<div>...<strong>...</strong> ...</div>

Set some styles for them.

body {
padding: 2vw;
}
div {
background: #f36;
padding: 2rem;
font-size: 2rem;
color: #fff;
margin-bottom: 3rem;
}
strong {
font-size: 3rem;
}

The effect seen is as follows:

This is exactly what we want. The div explicitly specifies the values of the background , padding , font-size , color , and margin-bottom properties, among which background , padding , and margin-bottom are non-inherited properties, while color and font-size are inherited properties. In addition, the div has a Client proxy style : display: block , which is also a non-inherited property. In addition, the strong element sets an inherited property font-size , which by default inherits the color property of the common parent element, as well as the html element's font-family and line-height properties. Of course, the strong element also has a Client proxy style font-weight: bold .

The effect seen above is the effect we usually use. If at this time, we set all: inherit; in div and strong at the same time, the effect obtained is completely different from the previous effect:

At this time, div and strong reset the properties they set at the beginning, and inherited some properties of their respective parent elements:

  • The div element inherits the padding , font-family , and line-height of the body element, as well as the style color , background , and display of the body
  • The strong element inherits the style of the div element

So the final result you see is like the above picture. We set the value of all to initial :

At this time, the div and strong styles are reset to the corresponding initial style, that is, the default style of the corresponding property, including the proxy Client style is also reset to the initial value of the corresponding property.

Finally, let’s look at the effect of setting the value of all to unset . In the following example, I only set all: unset on the strong element, and its effect is enough to explain everything:

The first one in the effect does not set all: unset , and the second one sets all: unset . At this time, the strong element's font-size and font-weight inherit the font-size and font-weight of its parent element.

All is sometimes a property in CSS, such as here it is a property, but sometimes it is also the value of some properties in CSS. For example, the all we often use in transitions is the property value. So far, the all property in CSS has also been supported by many browsers.

If you want to learn more about initial , inherit , revert , and unset , read Modern CSS 's Explicit CSS Defaults: inherit , initial , revert and unset .

Calculation of CSS styles

The final value of a CSS property is calculated in four steps:

  • Determine the value by specifying, often called the specified value (Specified Value)
  • Then process to get a value for inheritance, often called computed value (Computed Value)
  • Then, if necessary, convert it to an absolute value, often called the used value.
  • Finally, according to the local environment limit tmhj conversion, often referred to as the actual value (Actual Value)

So what are specified values , calculated values , applied values and actual values ?

Specified value

The user agent must first assign a specified value to each attribute according to the following mechanism (in order of priority):

  • If the stack produces a value, use it
  • Otherwise, if the attribute is inherited and the element is not the root element of the document tree, the calculated value of its parent element is used
  • Otherwise, use the initial value of the attribute, and the initial value of each attribute is indicated in the attribute definition

Calculated value

Specified values are processed into calculated values by cascading, for example, URIs are converted to absolute, em and ex units are calculated as pixels or absolute lengths. Calculating a value does not require a user agent to render the document. If user agent rules cannot process an absolute URI, the calculated value of the URI is the specified value.

The computed value of an attribute is determined by the Computed Value line in the attribute definition. When the specified value is inherited , the computed value definition can be calculated according to the rules introduced in inheritance. Even if the attribute does not apply (to the current element), its computed value exists and is defined in the 'Applies To' line. However, some attributes may define the computed value of an element attribute based on whether the attribute applies to the element.

Applied value

When processing calculated values, try not to format the document as much as possible. However, some values can only be determined when the document layout is complete. For example, if the width of an element is a specific percentage of its containing blocks, this width cannot be determined until the width of the containing blocks is determined. Applying values is the result of processing the remaining dependencies (values) of the calculated value into absolute values.

Actual value

In principle, the application value should be used for rendering, but the user agent may not be able to utilize the value in the given environment. For example, the user agent may only be able to render the border with integer pixel widths, so the calculated value of the width must be approximated, or the user agent may be forced to only use black and white tones instead of full color. The actual value is the applied value after approximation.

Summary

This article covers a lot of content, hoping to help everyone understand how styles are influenced by our writing and application, especially the cascading and inheritance of CSS. In practical use, if these concepts and methods are well applied, it can better help everyone write less style code and be easier to maintain their own CSS code.

Use a picture to represent as follows:

--

--

w3cplus

Author of "Modern CSS," "Modern Web Layout," "In-Depth CSS Defense," and "A Journey into Web Animation!"