CSS CSS Foundation & Core Concepts Inheritance in CSS

When I first started writing CSS, I made a mistake I see beginners repeat all the time.

I’d set font-family: 'Inter', sans-serif on my <body> tag, then immediately start doubting myself. “Does my <h1> actually pick that up? What about the <p> tags? What if I have a <span> nested three levels deep?” So I’d just set the font-family on every single element. Just to be safe.

I was writing 20 lines of CSS where 2 would do the exact same job.

That’s what CSS inheritance is about: certain properties automatically flow from a parent element down to its children, grandchildren, and beyond. Set it once at the top, and everything inside picks it up, with no extra code needed.

In our previous article on the CSS Cascade, we talked about how CSS decides which style wins when multiple rules conflict. Inheritance is a different layer of that same system. It answers a slightly different question: where does a value come from when no rule explicitly sets it?

By the end of this article, you’ll know:

  • Which CSS properties inherit by default, and which ones don’t
  • How to force any property to inherit using the inherit keyword
  • How to block inheritance with initial
  • What unset does and when it’s the smarter choice
  • How the all shorthand lets you reset everything at once
  • How to put all of this together to write noticeably less CSS

Let’s go.


01What CSS Inheritance Actually Is

Every HTML page is a tree. <html> sits at the root, <body> lives inside it, and then all your headings, paragraphs, divs, and spans branch out from there, each one nested inside another.

Inheritance means: when certain CSS properties are set on a parent element, those properties automatically apply to every descendant, children, grandchildren, great-grandchildren, all the way down.

Think of it like family traits. You didn’t choose your eye color, it passed down from your parents. CSS works the same way for specific properties. You set color on <body>, and every text element inside inherits that color automatically.

Here’s the simplest example:

body {
  color: #94a3b8;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  line-height: 1.6;
}

Every <h1>, <h2>, <p>, <li>, <span> on your page will automatically use this color and font. You didn’t write a single rule targeting them. They inherited it.

The one important thing to get straight early: not every CSS property inherits. The CSS spec made a deliberate decision about which ones should pass down. Once you understand the logic behind that decision, the whole system clicks into place.


02The Inheritance Tree: Seeing It Flow in Real Code

Take this HTML structure:

<body>
  <main>
    <h2>CSS Inheritance</h2>
    <p>Set it once. <span>It flows everywhere.</span></p>
  </main>
</body>

Add this CSS:

body {
  color: #94a3b8;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
}

Here’s exactly what happens in the inheritance tree:

Inheritance Flow: Styles Travel Down the DOM Tree

<body> — where you set the styles
color: #94a3b8
font-family: ‘Inter’
font-size: 16px
<main>
✓ inherits color
✓ inherits font-family
✓ inherits font-size
<h2>
✓ inherits color
✓ inherits font-family
⚠ browser overrides font-size (2em)
<p>
✓ inherits color
✓ inherits font-family
✓ inherits font-size
<span> inside <p>
✓ inherits everything from <p>

Notice the amber warning on <h2>. The font-size property does inherit, but the browser’s built-in stylesheet has a specific rule that makes headings larger. That browser rule beats the inherited value because it’s more specific in the cascade. This is exactly what we covered in our CSS Cascade article. Inheritance only kicks in when nothing else, including browser defaults, has set a value for that element.


03Which CSS Properties Inherit by Default

The spec divides every CSS property into two categories: inherited and non-inherited. This isn’t a browser decision, it’s part of the CSS standard itself.

Text and Typography: The Natural Inheritors

The large majority of CSS properties that inherit by default are about text. Color, font, spacing, alignment. This makes complete sense: you want consistent typography across a section without setting it on every single child element.

✓ Inherits by Default

  • color
  • font-family
  • font-size
  • font-weight
  • font-style
  • font-variant
  • line-height
  • letter-spacing
  • word-spacing
  • text-align
  • text-indent
  • text-transform
  • text-shadow
  • list-style
  • visibility
  • cursor
  • word-break
  • white-space
  • direction

✗ Does NOT Inherit

  • margin
  • padding
  • border
  • border-radius
  • background
  • background-color
  • width / height
  • display
  • position
  • top / right / bottom / left
  • overflow
  • box-shadow
  • opacity
  • transform
  • flex / grid properties
  • z-index
  • outline
  • animation
  • float

The pattern is clear. Text-related properties inherit, layout and box-model properties don’t.

Why Layout Properties Refusing to Inherit is the Right Call

Imagine for a second that margin inherited. You’d set margin: 30px on a <section>, and every single paragraph, heading, and span inside it would also get 30px of margin on all sides. The page would look like it exploded.

Or if background-color inherited: you’d set a dark background on <body> and every nested div, every card, every button would also have that same dark background layered on top. You couldn’t give any element its own distinct background.

The spec made the right call. Text properties flow down because consistent typography is something you actually want. Layout properties stay put because layout needs precision and control at each individual level.

💡
A note on opacity: It’s not technically an inherited property, but it visually affects all children anyway. When you set opacity: 0.5 on a parent, the children appear semi-transparent too, because they’re rendered inside the parent’s compositing layer. The value isn’t inherited, but the visual effect bleeds through. If you only want to fade a background without affecting the text inside, use background: rgba() instead.

04Forcing Inheritance with the inherit Keyword

Here’s where it gets genuinely useful. Even if a property doesn’t inherit by default, you can force it to. The inherit keyword tells an element: “take whatever value your parent has for this property.”

.child {
  border: inherit;       /* border doesn't inherit by default, but now it will */
  background: inherit;   /* same here */
}

The inherit keyword works on absolutely any CSS property. Inherited or not. It always means the same thing: look at the parent and use its value.

The Most Common Real-World Use: Links Inside Colored Containers

You’ll hit this scenario constantly. A container has a custom text color, and you want the links inside to match. But the browser’s default stylesheet sets a specific color: blue (or similar) on anchor tags, and that browser default beats the inherited color from the parent.

<div class="notice">
  <p>Read our <a href="#">complete guide</a> for full details.</p>
</div>
.notice {
  color: #f59e0b; /* amber */
  background: rgba(245, 158, 11, 0.1);
  padding: 1rem 1.25rem;
  border-radius: 10px;
}

/* The <p> inside will be amber, it inherits from .notice */
/* But the <a> stays blue, the browser overrides it */

/* Fix: force the link to inherit the amber */
.notice a {
  color: inherit;
}

Here’s exactly what that looks like:

One line. The link now matches the container’s amber color. This is probably the most practical use of the inherit keyword in everyday CSS.


05Stopping Inheritance with the initial Keyword

initial goes in the opposite direction. Instead of reaching up to grab the parent’s value, it resets the property to the CSS specification’s original default value, completely ignoring both the parent and the browser.

body {
  color: #6366f1; /* indigo */
}

.hero-label {
  color: inherit; /* explicitly gets indigo from body */
}

.footnote {
  color: initial; /* ignores body's indigo, goes back to spec's initial value */
}

.hero-label will be indigo. .footnote will use the spec’s initial value for color, which in most environments resolves to the browser’s default text color (black or a system default, depending on OS).

⚠️
Important distinction: initial does NOT mean “the browser’s default.” It means the CSS spec’s initial value. For most properties those happen to be the same, but not always. For example: the spec’s initial value for display is inline. But browsers render <div> as block by default. Writing display: initial on a div gives you display: inline, not display: block. Keep that in mind.

You’d use initial when you want to fully detach an element from whatever its ancestors are passing down, and you don’t want to hard-code a specific value. Instead of writing color: black, you write color: initial and let the spec decide.


06The unset Keyword: A Smarter Middle Ground

unset is a hybrid of the two. The rule is simple:

  • If the property naturally inherits by default: unset acts exactly like inherit
  • If the property doesn’t naturally inherit: unset acts exactly like initial
.element {
  color: unset;      /* color inherits by default → same as color: inherit */
  font-size: unset;  /* font-size inherits by default → same as font-size: inherit */
  border: unset;     /* border doesn't inherit → same as border: initial */
  margin: unset;     /* margin doesn't inherit → same as margin: initial */
}

On its own, unset can feel like a verbose way to write something you could just express directly. But it becomes genuinely powerful when paired with the all shorthand, which we’re about to get to.

Here’s a side-by-side visual of all three keywords in action:

Live Comparison: inherit vs initial vs unset on color

.parent

color: #ec4899 — this parent is styled pink

.child-one

color: inherit

I’m pink, too.

Grabs parent’s value directly.

.child-two

color: initial

I’m default grey.

Ignores parent. Goes to spec default.

.child-three

color: unset

I’m pink, too.

color inherits by default, so unset = inherit here.


07The all Shorthand: A Complete Property Reset in One Line

This one genuinely surprised me the first time I saw it in use.

The all property applies a single value to every CSS property on an element at once. Background, color, font, border, margin, padding, display, position: every single property, all in one go.

.clean-component {
  all: unset;
}

One line. Every property is now reset. This is most useful when you’re building custom buttons, form inputs, or any component where browser defaults get in the way:

.custom-btn {
  all: unset; /* wipe the browser's button styles completely */

  /* Now build your own from a clean foundation */
  display: inline-flex;
  align-items: center;
  padding: 0.6rem 1.25rem;
  background: #6366f1;
  color: #ffffff;
  font-size: 0.9rem;
  font-weight: 600;
  border-radius: 8px;
  cursor: pointer;
  letter-spacing: 0.01em;
}

No wrestling with outline, appearance, font, or any of the browser’s default button quirks. You start fresh and build exactly what you want.

all: unset vs all: initial vs all: revert

Here’s how the three most common all values differ:

The all shorthand: three different reset levels on a button


/* No all property — fully styled */

/* all: unset — no styles remain */

/* all: revert — browser defaults restored */
/* Resets every property to the CSS spec's initial value.
   Nothing inherits, nothing uses browser defaults. */
.hard-reset {
  all: initial;
}

/* Smarter reset: non-inherited properties go to initial,
   inherited properties still inherit from the parent. */
.smart-reset {
  all: unset;
}

/* Rolls back everything to what the browser's built-in
   stylesheet would set. Undoes your custom rules only. */
.browser-reset {
  all: revert;
}
💡
Which to pick: For custom buttons and form components, all: unset is usually what you want. It gives you a clean canvas while still respecting inherited text properties from the parent. Use all: revert when you’ve over-specified a component and want to undo just your own rules without rebuilding from absolute zero.

08Writing Far Less CSS by Leaning on Inheritance

This is the part that actually changes how you work. Let me show you a real contrast.

The “Set It Once” Approach: Stop Repeating Typography Rules

Here’s what a lot of beginners write when they want consistent fonts and colors across a page:

✗ Without Inheritance (Repetitive)
h1 {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}
h2 {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}
p {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}
li {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}
span {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}
✓ With Inheritance (Clean)
body {
font-family: ‘Inter’, sans-serif;
color: #e2e8f0;
}

/* That’s it. Every element
inside body inherits both.
Update once, it changes
everywhere. */

These two code blocks produce identical results. The inheritance version is five times shorter, and if the brand font changes, you update it in exactly one spot.

A Real Card Component: Inheritance Doing the Heavy Lifting

Let’s look at something more realistic. A card component where inheritance handles the shared styles, and you only override what actually needs to differ:

<div class="card">
  <h3 class="card-title">CSS Inheritance</h3>
  <p class="card-body">Set styles once. Let them flow down naturally.</p>
  <a class="card-link" href="#">Read more →</a>
</div>
/* Set shared defaults at the component root */
.card {
  background: #1e293b;
  padding: 1.5rem;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.05);

  /* All text inside will inherit these */
  color: #f8fafc;
  font-family: 'Inter', sans-serif;
  line-height: 1.6;
}

/* Only specify what actually needs to be different */
.card-title {
  font-size: 1.2rem;
  font-weight: 700;
  margin: 0 0 0.5rem 0;
  /* color and font-family: inherited from .card */
}

.card-body {
  font-size: 0.92rem;
  color: #94a3b8; /* slightly softer, so we override */
  margin: 0 0 1rem 0;
  /* font-family: inherited from .card */
}

.card-link {
  font-size: 0.88rem;
  font-weight: 600;
  color: inherit; /* force link to use .card's white, not browser's blue */
  text-decoration: underline;
}

The key line is color: inherit on .card-link. Without it, the browser’s default blue link color would override the white we set on .card. With it, the link uses the card’s color. Clean, intentional, and takes exactly one line.

This is the real-world pattern: set defaults at the component level, let children inherit the shared styles, and only override what genuinely needs to be different.


09CSS Inheritance Keywords: Quick Reference

Keyword What It Does Best Used When
inherit Takes the exact computed value from the parent element A property doesn’t inherit by default but you want it to (links in colored containers, borders, etc.)
initial Resets to the CSS specification’s original initial value You want to fully detach from the parent without writing a specific value
unset Acts as inherit if the property naturally inherits; acts as initial if it doesn’t Combined with all, or when you’re unsure which type of reset you need
revert Rolls back to the browser’s built-in user-agent stylesheet value You’ve over-specified something and want browser defaults back without rebuilding from zero
all Applies one of the above values to every CSS property on the element simultaneously Custom buttons, form inputs, or any component where browser defaults need a full wipe

If you want to look up whether any specific property inherits, MDN’s CSS Inheritance reference is the definitive source. Every property page on MDN lists whether it’s inherited right in the definition block.


10Three Habits to Start Using Today

Here are three concrete things you can apply immediately:

1. Set typographic defaults on body, not everywhere. Put font-family, color, font-size, and line-height on body first. From there, only override what specific sections or components need to differ.

2. When a link looks wrong inside a colored section, reach for color: inherit first. It’s almost always the right solution, and it’s one line. This is the most commonly needed use of inherit in everyday CSS work.

3. When building custom buttons or inputs, start with all: unset. Browser styles on interactive elements are notoriously inconsistent across operating systems. A clean slate is much easier to work with than trying to undo a dozen defaults one by one.


11Up Next: Selectors, the Building Blocks of Every CSS Rule

We just wrapped up Batch 1 of this CSS series. From the introduction to CSS, through how the browser renders styles, through the cascade, and now inheritance: you now have a solid mental model of how CSS actually works at its core.

In Batch 2, we move into Selectors. The first article covers Universal, Type, Class, and ID Selectors: the four fundamental selector types, when each one is the right tool, and why over-relying on IDs will create headaches later. It’s a short but important topic that all the more advanced selector work builds on.

If you’ve been reading these articles in order, you’re in a genuinely good position. You understand the cascade, you understand inheritance, you know how to control the flow of styles. Most beginners skip these foundational concepts entirely and spend months confused about why their CSS “isn’t working.” You won’t have that problem.

Keep going. It gets more interesting from here.

Inheritance in CSS

Choose a difficulty to load your challenge

Loading challenge…

Ready to Practice?

Select a difficulty level above to load your challenge.

Easy

1
Live Preview
Great Work!