CSS Inheritance
Inheritance is the mechanism by which certain CSS property values, when not explicitly set on an element, are copied down from that element’s parent instead of falling back to the property’s built-in default. It exists because many typographic properties — the ones concerned with how text looks and reads — naturally belong to a whole block of content, not to each individual element inside it. Without inheritance you would have to repeat font-family, color, and line-height on every single element on a page. Understanding exactly which properties inherit, and how inheritance interacts with the cascade, saves you from a lot of confusing styling bugs.
Overview: How Inheritance Works
Every CSS property is classified by the specification as either inherited or not inherited — this is a fixed characteristic of the property itself, not something you choose per rule. Text- and list-related properties such as color, font-family, font-size, font-weight, line-height, letter-spacing, text-align, visibility, cursor, list-style, and white-space are inherited by default. Box-model and layout properties such as margin, padding, border, width, height, background, position, and display are not inherited, because each box needs its own independent size, spacing, and background — a paragraph inside a bordered card shouldn’t automatically grow its own border just because its container has one.
Inheritance sits at the very bottom of the value-resolution algorithm the rendering engine runs for every element and every property. For each property on each element the browser works through these steps in order:
- Collect every declaration from every stylesheet (author, user, and browser/user-agent) that matches the element, and pick a winner using the cascade — origin and importance first, then selector specificity, then source order.
- If a declaration won, that becomes the element’s specified value for that property, and inheritance never gets a chance to run.
- If nothing matched, the browser checks whether the property is inherited. If it is, the element’s specified value becomes a copy of the parent’s computed value for that property.
- If nothing matched and the property is not inherited, the browser falls back to the property’s initial value defined in the spec (for example the initial value of
border-styleisnone).
This ordering explains a very common surprise: an inherited value always loses to an explicit declaration, even a low-specificity one, and even one written into the browser’s own default stylesheet. That is exactly why links don’t automatically pick up their surrounding text color — see Common Mistakes below.
Values aren’t just copied verbatim, either — the browser inherits the parent’s computed value, so relative units resolve as they travel down. If a parent has font-size: 20px and a child has no font-size declared, the child inherits the computed 20px, not a formula. But if the child declares font-size: 1.5em, that em is resolved against the parent’s computed font-size (20px × 1.5 = 30px), and it is this resolved 30px pixel value — not the em itself — that gets passed further down to any grandchildren.
Syntax
CSS gives you four keywords that let you take explicit control of inheritance instead of leaving it to a property’s default behavior. You can assign any of them to almost any property.
| Keyword | Effect |
|---|---|
inherit |
Forces the property to take the parent element’s computed value, even for properties that are not inherited by default (for example border: inherit;). |
initial |
Resets the property to its specification-defined initial value, ignoring both the cascade and inheritance. |
unset |
Acts like inherit if the property is naturally inherited, or like initial if it is not — effectively \”do what would happen with no author styles at all.\” |
revert |
Rolls the property back to the value it would have from the user-agent (browser default) stylesheet or user styles, skipping only author styles. |
selector {
property: inherit | initial | unset | revert;
}
The block above just illustrates the shape of the syntax — you pick exactly one keyword per declaration, not all four.
Examples
Example 1: Plain inheritance with no keywords
Applied to a wrapper containing an <h2>, a <p>, and a <strong> inside the paragraph:
.article {
color: #2b2b2b;
font-family: Georgia, \"Times New Roman\", serif;
line-height: 1.6;
}
Result: the heading, the paragraph, and the bold word all render in the same dark gray Georgia serif text with 1.6 line spacing, even though none of them declare color, font-family, or line-height themselves.
This works because those three properties are inherited by default: every descendant with no competing declaration copies the computed value down from .article.
Example 2: Taking explicit control with inherit
Applied to a colored panel containing a <button>:
.panel {
color: #ffffff;
background-color: #1e40af;
font-family: inherit;
}
.reset-btn {
color: inherit;
border: 2px solid currentColor;
background: transparent;
font: inherit;
}
Result: the panel shows white text on a dark blue background. The button inside it loses the browser’s default gray button chrome: it becomes transparent with a 2px white border and white text that matches the panel’s font, instead of the platform’s default form-control appearance.
Browsers intentionally opt form controls like <button>, <input>, and <select> out of normal color and font inheritance in their user-agent stylesheets, so color: inherit and font: inherit are needed to pull those values back in from the surrounding page. The border then uses currentColor, which always matches whatever the resolved color value is.
Example 3: Theming with inherited custom properties
:root {
--brand-color: #0f766e;
--brand-font: \"Segoe UI\", system-ui, sans-serif;
}
.theme-dark {
--brand-color: #5eead4;
color: var(--brand-color);
font-family: var(--brand-font);
}
.theme-dark .card-title {
color: var(--brand-color);
}
Result: text inside .theme-dark, including anything matched by .theme-dark .card-title, renders in teal (#5eead4). Anything outside .theme-dark that also reads var(--brand-color) still renders in the root’s darker teal (#0f766e).
Custom properties are inherited by default, so redeclaring --brand-color on .theme-dark changes the value that flows down to every descendant reading that variable, without needing to override each selector individually. This is one of the most practical uses of inheritance in modern CSS.
How It Works Step by Step
Consider four nested elements: <body> with no font-size rule, a <section class=\"sidebar\"> with font-size: 0.875em, a <p> inside it with no font-size rule, and a <small> inside the paragraph with font-size: 0.9em. Here is how the browser resolves each one:
- <body>: no author rule for font-size matches, and there’s no parent to inherit from, so it falls back to the initial/user-agent value: 16px.
- <section class=\”sidebar\”>: declares 0.875em. An em unit is relative to the parent’s computed font-size, so 0.875 × 16px = 14px. That 14px becomes the section’s computed value.
- <p>: no font-size rule matches. Since font-size is inherited, its specified value becomes a copy of the parent’s (section’s) computed value: 14px.
- <small>: declares 0.9em, relative to its own parent, the paragraph, whose computed size is 14px. 0.9 × 14px = 12.6px.
Notice that each level’s em is computed relative to its immediate parent’s already-resolved size, and it is that resolved pixel value which continues down the tree. This is why chaining several relative font-sizes through deeply nested components compounds — each em multiplies the previous result — and it’s a common source of unexpectedly tiny or huge text in complex layouts.
Common Mistakes
Mistake 1: Expecting links to inherit the surrounding text color
body {
color: #1a1a1a;
}
Body text renders in dark gray as expected, but any <a> elements keep the browser’s default link color (typically blue) instead of matching the body text. That’s because the user-agent stylesheet ships its own declaration for link color, and any declared value — even one from the browser’s built-in stylesheet — beats an inherited value in the cascade. Inheritance only fills in a property when nothing else declared a value for it.
Corrected:
body {
color: #1a1a1a;
}
a {
color: inherit;
}
Links now render in the same dark gray as the surrounding body text, because color: inherit explicitly overrides the user-agent stylesheet’s link color instead of waiting for ordinary inheritance to apply.
Mistake 2: Assuming box-model properties inherit
.card {
border: 1px solid #ccc;
padding: 20px;
}
Only the element with class .card gets the light gray border and padding. A paragraph or any other element nested inside the card stays unbordered and unpadded, because border and padding are not inherited properties — each box computes its own box-model values independently.
Corrected:
.card,
.card .highlight {
border: 1px solid #ccc;
}
.card {
padding: 20px;
}
Both .card and any descendant carrying the .highlight class now show the border, because the selector explicitly targets both elements rather than relying on inheritance to spread a box-model property down the tree.
Best Practices
- Rely on inheritance for typography: set
font-family,color, andline-heightonce near the root (for example onbody) instead of repeating them on every element. - Use
color: inheritandfont: inheritexplicitly on form controls (button,input,select,textarea), since browsers intentionally opt them out of normal font and color inheritance. - Reach for custom properties when you want themeable values to cascade through arbitrary nesting depth instead of overriding many individual selectors by hand.
- Don’t fight inheritance with heavy universal-selector resets; prefer a small, well-understood reset plus a few deliberate
inheritdeclarations where you actually need them. - Remember that non-inherited properties (
margin,padding,border,background,position,width,height) must be set explicitly on every box that should have them. - Use
unsetorrevertwhile debugging to quickly see what an element would look like without your authored override in place.
Practice Exercises
- Build a
<div class=\"post\">containing a heading and two paragraphs. Setcolor,font-family, andline-heightonly on.post, then reason through which child elements display those styles and why, based on the inheritance rules above. - Given a
<button>sitting inside a colored panel, write the two declarations needed so the button’s text color and font match the panel’s inherited values instead of the browser’s default button appearance. - Define a custom property
--accenton:root, then override it on a single wrapper class further down the tree. Predict which descendants would show the new value if three different sub-components each usevar(--accent)in acolordeclaration.
Summary
- Inheritance copies a parent’s computed value down to descendants, but only for properties the specification marks as inherited — mostly typography- and list-related properties.
- Box-model and layout properties (margin, padding, border, background, width, height, position) are not inherited by default.
- Any actual declared value, from any origin, however small its specificity, beats an inherited value in the cascade.
inherit,initial,unset, andrevertgive you explicit control over any property’s inheritance behavior.- Relative units like
emresolve against the parent’s computed value at each level, so nested relative sizing compounds down the tree. - Custom properties are inherited by default, making them a powerful tool for cascading theme values through deep nesting.
