You can view RSS feeds from my friends and colleagues.
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
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.
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.
When we get to the unset
value the distinction between inheriting and non-inheriting properties becomes important. unset
has a different effect on them.
unset
means inherit
.unset
means initial
.revert
, the newest of these keywords, also distinguishes between inheriting and non-inheriting properties.
revert
means inherit
.revert
reverts to the value specified in the browser style sheet.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
.
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:
display: inherit
: all elements now inherit their display value from the body. Since the body has display: block
all elements get that value, whether that makes sense or not.display: initial
: the initial value of display is inline
. Therefore all elements get that value, whether that makes sense or not.display: unset
: display does not inherit. Therefore this behaves as initial
and all elements get display: inline
.display: revert
: display does not inherit. Therefore the defaults of the browser style sheet are restored, and each element gets its proper display — except for the dl
, which I had given a display: grid
. This value is now supplanted by the browser-provided block
.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:
color: inherit
: all elements, including <button>
s, now inherit their colour from the body, which is blue
. So all text becomes blue.color: initial
: since the initial value of color
is black, all elements, including <button>
s, become black.color: unset
: color inherits. Therefore this behaves as inherit
and all elements, including <button>
s, become blue.color: revert
: This is the weird one. All elements become blue, except for <button>
s, which become black. I don’t understand why. Since colors inherit, I expected revert
to work as inherit
and the buttons to also become blue. But apparently the browser style sheet of button {color: black}
(more complicated in practice) is given precedence. Yes, revert
should remove author styles (the ones we write), and that would cause the black from the browser style sheet to be applied, but only if a property does not inherit — and color does. I don’t know why the browser style sheet is given precedence in this case. So I’m going to cop out and say form elements are weird.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.
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.
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 seem to have left pieces of myself scattered around the internet. This is my attempt to pull some of those pieces together.