Looking for a Front-End Developer and Design Systems Practitioner?

I currently have some availabilty. Let's talk!.

Always Twisted

Making Context-Aware Components: How CSS inherit() Could Simplify Design Systems

Spotted on social media an ‘intent to ship’ post from the Blink team for a new inherit() CSS function. A quick read of what is within the Chrome Platform Status post, and what was in the W3C specification had my mind buzzing on how this can help us to build better, more ‘context aware’ components for our Design Systems moving forward.

So, with some coffee and the ideas popping I thought I’d write a short post on the Johnny Ball of it all.

CSS custom properties already let developers expose and use values from a Design System (often surfaced as design tokens). A child element can read a value set on an ancestor simply by referencing the same custom property via the cascade, but that isn’t a direct way to reference the parent’s computed custom property for in‑place derivation. The new inherit() function looks to help fill that gap. It will explicitly take the parent element’s computed custom‑property value so the child can derive a related value (for example, scale, offset, or colour mix) relative to the parent without duplicating tokens or relying on extra overrides.

inherit() is a CSS Values Level 5 feature that reads a parent’s computed custom property (with an optional fallback, e.g. inherit(--foo, 16px)). It returns the parent’s resolved value, and if the immediate parent doesn’t set one, the value comes from the nearest ancestor that does.

This parent-focused behaviour could be exactly what design systems need: components can derive spacing, density, colour and motion from their container, so you avoid duplicating tokens and get a predictable, context-aware user interface.

But, why not make more custom properties?

It’s tempting to create Design Tokens for everything you think you’d ever need and add bespoke custom properties everywhere, but inherit() could give us a cleaner alternative. Rather than proliferating tokens, derive local values from a small set of region-level properties so components can stay simple and predictable.

A practical example, making “the card”: from custom properties to inherit()

Let’s look at a focused example where we can replace a multitude of bespoke custom properties with the new inherit() function.

First, let’s take a simple card component:

Code languagehtml
<article class="card">
<img src="" alt="">
<div class="card__content">
<h2>A Card Title</h2>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Unde tempore veritatis illum voluptas.</p>
<a href="">Click here</a>
</div>
</article>

And traditionally (however, more common recently) we would make use of existing custom properties in our code (perhaps from Design Tokens), something like:

Code languagecss
:root {
--spacing-200: .5rem;
--spacing-400: 1rem;
--spacing-600: 1.5rem;
}

Custom Properties Everywhere

The first version of our card will set every spacing value explicitly with a custom property.

Code languagecss
.card {
--card-spacing: var(--spacing-400);
--card-gap: var(--spacing-200);
--card-heading-gap: var(--spacing-400);
padding: var(--card-spacing);
}
.card__content > * + * {
margin-top: var(--card-gap);
}
.card__content h2 + * {
margin-top: var(--card-heading-gap);
}

One Custom Property to rule all spacing

We could tidy this up and reduce the duplication of custom properties inside the card by using some maths. The relationships are now explicit but also local.

Code languagecss
.card {
--card-spacing: var(--spacing-400);
max-width: 300px;
padding: var(--card-spacing);
border: 1px solid #000;
background: #fff;
}
.card__content > * + * {
margin-top: calc(var(--card-spacing) * 0.5);
}
.card__content h2 + * {
margin-top: var(--card-spacing);
}

Or we could start using inherit()

Code languagecss
.card {
--card-spacing: var(--spacing-400);
max-width: 300px;
padding: inherit(--card-spacing);
border: 1px solid #000;
background: #fff;
}
.card__content > * + * {
margin-top: calc(inherit(--card-spacing) * 0.5);
}
.card__content h2 + * {
margin-top: inherit(--card-spacing);
}

You can swap var() for inherit() here, but doing so on the card itself is redundant unless the parent provides a token.

Inheriting from the parent

Where this could truly be revolutionary on how we author CSS in component libraries and Design Systems is when can make use of an element’s parent custom properties. Say we have a generic container regions that has three padding options.

Code languagecss
:root {
--spacing-200: .5rem;
--spacing-400: 1rem;
--spacing-600: 1.5rem;
}
.region--compact { --gap: var(--spacing-200); }
.region--default { --gap: var(--spacing-400); }
.region--comfortable { --gap: var(--spacing-600); }

With inherit() we can make use of the --gap custom property so that our cards match the look and feel of the parent container.

Code languagecss
.card {
/* this will inherit from the parent --gap value */
padding: inherit(--gap, 1rem);
}
.card__content > * + * {
margin-top: calc(inherit(--gap, 1rem) * 0.5);
}
.card__content h2 + * {
margin-top: inherit(--gap, 1rem);
}

The container region sets the --gap, the card makes use of it with inherit(), cards inside .region--compact become tighter, those in .region--comfortable become more spacious automatically. There are no ‘per card’ overrides like .card--compact or .card--comfortable required.

We could, although I think it would make things more complex, also mix all options together. Making use of inherit() for context-aware design decisions and local component specific custom properties.

Code languagecss
:root {
--spacing-200: .5rem;
--spacing-400: 1rem;
--spacing-600: 1.5rem;
}
.region--compact { --gap: var(--spacing-200); }
.region--default { --gap: var(--spacing-400); }
.region--comfortable { --gap: var(--spacing-600); }
.card {
--card-pad: inherit(--gap, 1rem);
--card-gap: calc(inherit(--gap, 1rem) * 0.5);
--card-heading-gap: inherit(--gap, 1rem);
padding: var(--card-pad);
}
.card__content > * + * {
margin-top: var(--card-gap);
}
.card__content h2 + * {
margin-top: var(--card-heading-gap);
}

Choices and Fallbacks

When you adopt ⁠inherit() treat CSS custom properties as the runtime surface your components read from — not the canonical design-token source. Keep the runtime surface small: expose a few region-level properties that express layout and theme intent, and let components derive their implementation values from those.

At the region level expose a tight set of properties for values that change by context: —gap, —density, —base-size, —accent. These are the values components should inherit from their container and treat as authoritative for regional behaviour.

Inside components, keep implementation property names scoped and minimal (for example —card-gap, —btn-pad, —chip-accent). Derive those values from the nearest ancestor using ⁠inherit() rather than adding new global properties. Example:

Code languagecss
.card {
/* derive a card-scoped value from the region token */
--card-gap: calc(inherit(--gap, 16px) * 0.75);
padding: var(--card-gap);
}

Use consistent units (rem or px) so ⁠calc() is predictable, and include sensible fallbacks in ⁠inherit() so components work when a region property is missing or in older browsers. Document ownership: list which properties belong on root, which on page/region wrappers, and which are component implementation details. That keeps naming clear and makes it obvious what ⁠inherit() will read and what components compute.

Dealing with non‑supporting browsers

inherit() will be a runtime cascade feature and support will vary while browsers implement it. The goal of any front-end developer is to make components that behave well everywhere today while progressively enhancing where CSS features like inherit() become available.

Here’s a couple of ways to approach using inherit()

We could use @supports() to detect the lack of inherit() and provide a simple fallback path.

Code languagecss
@supports (not (padding: calc(inherit(--gap)))) {
.card { padding: var(--fallback-card-pad, 12px); }
}

We could provide some defaults first and then place the inherit() rule afterwards

Code languagecss
.card {
/* baseline for old browsers */
padding: var(--card-pad, 1rem);
/* enhanced when inherit() is available (overrides the baseline) */
padding: inherit(--gap, var(--card-pad, 1rem));
}

Where to "+1" implementation

If you want this CSS feature implemented into web browsers you can upvote them here:

To end…

The new inherit() CSS feature can make your components naturally context-aware. For Design Systems I think it promises the opportunity for fewer design tokens, fewer overrides and simpler CSS-first adaptations to layouts, typography, theming, and motion.

Unsure how to structure your design tokens for scalability and future growth?

I can help define a scalable structure for your tokens, ensuring they grow with your system.

get in touch!