Where CSS Values Come From When You Set None: How Inheritance Works
Learn how browsers decide whether a property inherits, why children receive the parent's computed value, and how inherit and initial give you explicit control.
This article is published in English.
Sources: How Inheritance Works in CSS Internally at Algorithmic Level.
You set color on a wrapper, and the paragraph inside turns red even though no rule ever mentioned it. Meanwhile the padding you put on that same wrapper stays exactly where it is. Both behaviours come from CSS inheritance, the step that fills in a value when an element has none of its own. This guide explains which properties inherit, what exactly gets passed down (the answer surprises many people when percentages are involved), how the inherit and initial keywords change the default, and where inheritance fits among the other stages that turn a stylesheet into final values.
A value that nobody declared
Start with a rule on a parent element:
.parent {
color: red;
}
and markup where a paragraph sits inside that parent:
<div class="parent">
<p>Hello World</p>
</div>
No rule sets color on the <p>, yet its text renders red. By the time the browser reaches this point it has already resolved competing declarations through the cascade and processed their values. What remains is deciding, for each property on each element, whether the value should come from the element's own declarations or from somewhere up the tree. Inheritance is the mechanism that makes that decision.
Which values travel down the tree
Inheritance lets certain property values pass from a parent element to its children, and from there to their children in turn:
Parent
↓
Child
↓
Grandchild
Not every property participates. Properties that describe text are the classic inherited ones, such as:
font-family
font-size
color
This is why you rarely need to repeat typography on every element. Rather than setting the same colour on the body and then again on each kind of child element:
body {
color: #333;
}
h1 {
color: #333;
}
p {
color: #333;
}
li {
color: #333;
}
you can declare it once, high in the document tree, and let inheritance carry it down. The result is less CSS and a single place to change when the design changes. Setting base font and colour on body or :root is the everyday application of this idea.
The decision the browser makes for each property
The logic becomes clearer when you look at the case where no declaration applies to an element. For every property, the browser effectively asks two questions in order.
First: is there a cascaded value? If any declaration for this property won the cascade on this element, that value is used and inheritance never comes into play.
Second, only if the answer was no: is this property inherited? That depends entirely on the property, because each one is defined as either inherited or not inherited by the specification. Some examples of inherited properties:
color → inherited
font-family → inherited
font-size → inherited
and some that are not inherited:
margin → not inherited
padding → not inherited
The split is deliberate. Picture a world in which margin inherited: a single margin on a section would be copied onto every paragraph, list and link nested inside it, and spacing would compound at every level. Box-related properties like margin, padding, border and width are almost always things you want to set per element, while text-related properties are things you usually want to flow through a whole region. If a property does not inherit and has no cascaded value, it takes its initial value, the default defined for that property.
If you are unsure about a specific property, the "Inherited" entry in its reference documentation answers the question directly.
Children inherit the computed value, not the text you wrote
This is the most important detail in the whole process, and the one behind a well-known surprise. Suppose a parent sets a font size and a percentage line height:
.parent {
font-size: 20px;
line-height: 150%;
}
and a child overrides only the font size:
.child {
font-size: 25px;
}
What line height does the child end up with? An intuitive guess is that the child receives the percentage itself:
150%
and then resolves it against its own, larger font size:
150% of 25px
That is not how it works. What an element inherits is its parent's computed value, not the value as it was declared. For line-height, a percentage is resolved into a length at computed-value time. On the parent, the calculation is:
150% of 20px = 30px
so the parent's computed line height is:
30px
That absolute length is what flows down. The child, despite its bigger font, inherits a line height of:
30px
and not:
150% of 25px
The practical consequence is lines that look cramped whenever a descendant uses a larger font than the element where the percentage was declared. The same happens with em values for line-height, because they are also converted to lengths before inheritance. This is exactly why unitless line heights such as line-height: 1.5 are the common recommendation: a plain number is itself the computed value, so each descendant multiplies it by its own font size instead of inheriting a fixed length.
Properties that do not inherit fall back to their initial value
Now take a property from the other group:
padding
Padding does not inherit. So when a parent declares:
.parent {
padding: 20px;
}
a child does not quietly receive:
padding: 20px;
Instead, with no cascaded value and no inheritance, the child's padding uses the property's initial value, which for padding is:
0px
That holds unless some other rule explicitly gives the child a different padding. Knowing which bucket a property falls into saves time when debugging: if a value seems to "leak" into children, it is an inherited property; if it seems to "vanish" on children, it is a non-inherited one. Browser developer tools help here, because the Computed panel can show inherited values along with the ancestor they came from.
Forcing inheritance with inherit
You can override the default behaviour for any property. The inherit keyword tells the browser to take the parent's value even for a property that would not normally inherit:
.child {
padding: inherit;
}
With this rule, the child's padding matches the parent's computed padding. It is useful when you want a relationship between parent and child to be explicit, for example making a nested element reuse its container's border colour or spacing, or forcing form controls, which often do not inherit fonts by default in browser stylesheets, to use the surrounding typography with font: inherit.
This gives you two distinct routes to a parent's value. Automatic inheritance applies only to inherited properties and only when there is no cascaded value. Explicit inheritance with inherit is itself a declaration, so it wins the cascade like any other value and works for any property.
Resetting with initial
The companion keyword is:
initial
Applied to a property, it resets that property to its initial value as defined by the specification:
.child {
color: initial;
}
Be aware that "initial" means the specification's default, not the browser's default stylesheet or the value an element would otherwise have inherited. For color, the initial value is the system text colour (usually black), so color: initial does not restore the parent's colour; it cuts the element off from it. For display, the initial value is inline, which is why display: initial on a div turns it inline rather than back into a block.
Side by side, the two keywords do opposite jobs:
inherit → use the parent's value
initial → reset to the property's initial value
Together they give you explicit control over how values move through the tree. CSS also offers unset (which behaves like inherit for inherited properties and like initial for the rest) and revert (which rolls back to the browser's default stylesheet), both of which are often more useful than initial for resetting components.
Assembling the full picture
It helps to see inheritance alongside the steps that come before it. When several rules target the same element, the browser starts with the cascade, which compares declarations in this order:
Importance
↓
Specificity
↓
Source Order
The winner is the cascaded value. The value then moves through a sequence of processing stages until it becomes something layout and painting can use:
Declared Value
↓
Cascaded Value
↓
Specified Value
↓
Computed Value
↓
Used Value
↓
Actual Value
When no cascaded value exists, inheritance supplies the input for that sequence instead:
No Cascaded Value
↓
Can Property Be Inherited?
↓
Yes
↓
Parent's Computed Value
↓
Child
Strictly speaking, the specification places inheritance at the point where the specified value is determined: the specified value is the cascaded value if there is one, otherwise the parent's computed value for inherited properties, otherwise the initial value. From there the usual computed, used and actual stages continue. A simplified overall view of the CSS processing stage therefore looks like this:
CSS
↓
Cascade
↓
┌─────────────┴─────────────┐
↓ ↓
Importance Specificity
↓
Source Order
↓
Cascaded Value
↓
Value Processing
↓
Computed / Used / Actual
↓
Inheritance
↓
CSSOM
Read the vertical order in that diagram as a grouping of concepts rather than a strict timeline, since inheritance feeds the specified value rather than running after computed values. The key idea stands either way: there is far more happening between the stylesheet and the screen than "I wrote a rule and the browser applied it".
From stylesheet to pixels
Zooming back out, here is the complete journey with the CSS stage expanded:
HTML
↓
DOM
↓
CSS
↓
Cascade
↓
Specificity
↓
Value Processing
↓
Inheritance
↓
CSSOM
↓
DOM + CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Pixels
With that pipeline in mind, even a small rule carries a lot of implied work:
.title {
font-size: 2rem;
color: blue;
}
Before anything is drawn, the browser has to establish:
- which declarations apply to each element;
- which of them wins when they conflict;
- how specific the competing selectors are;
- what value each winning declaration actually resolves to (for example, what
2remis in pixels); - whether inheritance supplies values that were not declared;
- what sizes and positions ultimately follow from those values.
Only after all of that does the page move on to layout and rendering.
CSS as a system rather than a list of rules
Much of the confusion around CSS disappears once you stop treating it as isolated rules and start treating it as one system with clear responsibilities:
- Specificity explains why one declaration beats another.
- Source order settles ties when specificity is equal.
- Value processing turns what you wrote into values the browser can use.
- Inheritance explains where values come from when you did not write any.
Together they turn a stylesheet into a complete set of final values that feed layout and paint. Even a declaration as short as this one:
width: 66%;
passes through all of them. The browser resolves which rule applies, processes the percentage, relates it to the containing block and settles on a final width, all before a single pixel appears.
What remains after this is a separate question: where exactly each element goes. Widths, heights, normal flow, positioning and the box model belong to the visual formatting model, the next stage in the pipeline, which takes these final values and turns them into layout.
Key takeaways
- A property gets its value from the cascade first; inheritance is only consulted when no declaration applies.
- Each property is defined as inherited (mostly text-related:
color,font-*) or not inherited (mostly box-related:margin,padding). - Children inherit the parent's computed value, so percentage or
emline heights pass down as fixed lengths; prefer unitlessline-height. - Non-inherited properties without a declaration use their initial value, such as
0for padding. inheritforces inheritance for any property;initialresets to the specification default, not the parent's value;unsetandrevertare often the better reset tools.