Inherit, initial, unset, revert

Today we’re going to take a quick look at a few special CSS keywords you can use on any CSS property: inherit, initial, revert, and unset. Also, we will ask where and when to use them to the greatest effect, and if we need more of those keywords.

The first three were defined in the Cascading Level 3 spec, while revert was added in Cascading Level 4. Despite 4 still being in draft revert is already supported. See also the MDN revert page, Chris Coyier’s page, and my test page

I'm writing a CSS book.

inherit

The inherit keyword explicitly tells an element that it inherits the value for this declaration from its parent. Let’s take this example:

.my-div {
	margin: inherit;
}

.my-div a {
	color: inherit;
}

The second declaration is easiest to explain, and sometimes actually useful. It says that the link colour in the div should be the same as the text colour. The div has a text colour. It’s not specified here, but because color is inherited by default the div gets the text color of its parent. Let’s say it’s black.

Links usually have a different colour. As a CSS programmer you frequently set it, and even if you don’t browsers automatically make it blue for you. Here, however, we explicitly tell the browsers that the link colour should be inherited from its parent, the div. In our example links become black.

(Is this a good idea? Occasionally. But if you remove the colour difference between links and main text, make sure your links are underlined. Your users will thank you.)

Now let’s look at the margin: inherit. Normally margins don’t inherit, and for good reason. The fact that an element has margin-left: 10% does not mean all of its descendents should also get that margin. In fact, you most likely don’t want that. Margins are set on a per-case basis.

This declaration tells the div to use the margin specified on its parent, however. This is an odd thing to specify, and I never saw a practical use case in the wild. Still CSS, being ridiculously powerful, allows it.

In any case, that’s how the inherit keyword works. Using it for font sizes or colours may occasionally be a good idea. In other contexts - rarely.

And keep the difference between inheriting and non-inheriting properties in mind. It’s going to be important later on.

initial

The initial keywords sets the property back to its initial value. This is the value specified in the W3C specification for that property.

Initial values from the spec are a bit of a mixed bag. Some make sense, others don’t, really. float: none and background-color: transparent are examples of the first category. Of course an element does not have a background colour without you specifying one, nor does it float automatically.

Others are historically determined, such as background-repeat: repeat. Back in the Stone Age before CSS all background images repeated, and the CSS1 specification naturally copied this behaviour.

Still others are essentially arbitrary, such as display: inline. Why did W3C opt for inline instead of block? I don’t know, and it doesn’t really matter any more. They had to decide on an initial value, and while inline is somewhat strange, block would be equally strange.

In any case, the initial keyword makes the property revert to this initial value from the specification, whether that makes sense or not.

unset

When we get to the unset value the distinction between inheriting and non-inheriting properties becomes important. unset has a different effect on them.

revert

revert, the newest of these keywords, also distinguishes between inheriting and non-inheriting properties.

all

Finally, we should treat all. It is not a value but a property, or rather, the collection of all CSS properties on an element. It only takes one of the keywords we discussed, and allows you to apply that keyword to all CSS properties. For instance:

.my-div {
	all: initial;
}

Now all CSS properties on the div are set to initial.

Examples

The reaction of my test page to setting the display of all elements to the four keywords is instructive. My test script sets the following style:

body * {
	display: [inherit | initial | unset | revert] !important;
}

The elements react as follows:

Unfortunately the same test page also contains a riddle I don’t understand the behaviour of <button>s when I set color to the four keywords:

Practical use: almost none

The purpose of both unset and revert is to wipe the slate clean and return to the initial and the browser styles, respectively — except when the property inherits; in that case, inheritance is still respected. initial, meanwhile, wipes the slate even cleaner by also reverting inheriting properties to their initial values.

This would be useful when you create components that should not be influenced by styles defined elsewhere on the page. Wipe the slate clean, start styling from zero. That would help modularisation.

But that’s not how these keywords work. We don’t want to revert to the initial styles (which are sometimes plain weird) but to the browser style sheet. unset comes closest, but it doesn’t touch inherited styles, so it only does half of what we want.

So right now these keywords are useless — except for inherit in a few specific situations usually having to do with font sizes and colours.

New keyword: default

Chris Coyier argues we need a new value which he calls default. It reverts to the browser style sheet in all cases, even for inherited properties. Thus it is a stronger version of revert. I agree. This keyword would be actually useful. For instance:

.my-component,.my-component * {
	all: default;
	font-size: inherit;
	font-family: inherit;
	color: inherit;
}

Now we have a component that’s wiped clean, except that we decide to keep the fonts and colour of the main page. The rest is a blank slate that we can style as we like. That would be a massive boon to modularisation.

New keyword: cascade

For years now I have had the feeling that we need yet another keyword, which I’ll call cascade for now. It would mean “take the previous value in the cascade and apply it here.” For instance:

.my-component h2 {
	font-size: 24px;
}

.my-other-component h2 {
	font-size: 3em;
}

h2#specialCase {
	font-size: clamp(1vw,cascade,3vw)
}

In this (slightly contrived) example I want to clamp the font-size of a special h2 between 1vw and 3vw, with the preferred value being the one defined for the component I’m working in. Here, cascade would mean: take the value the cascade would deliver if this rule didn’t exist. This would make the clamped font size use either 24px or 3em as its preferred value, depending on which component we’re in.

The problem with this example is that it could also use custom properties. Just set --h2size to either 24px or 3em, use it in the clamp, and you’re done.

.my-component h2 {
	--h2size: 24px;
	font-size: var(--h2size);
}

.my-other-component h2 {
	--h2size: 3em;
	font-size: var(--h2size);
}

h2#specialCase {
	font-size: clamp(1vw,var(--h2size),3vw)
}

Still, this is but the latest example I created. I have had this thought many, many times, but because I didn’t keep track of my use cases I’m not sure if all of them could be served by custom properties.

Also, suppose you inherit a very messy CSS code base with dozens of components written at various skill levels. In that case adding custom properties to all components might be impractical, and the cascade keyword might help.

Anyway, I barely managed to convince myself, so professional standard writers will certainly not be impressed. Still, I thought I’d throw it out here to see if anyone else has a use case for cascade that cannot be solved with custom properties.

I'm writing a CSS book.

This is the blog of Peter-Paul Koch, web developer, consultant, and trainer. You can also follow him on Twitter or Mastodon.
Atom RSS

If you like this blog, why not donate a little bit of money to help me pay my bills?

Categories: