The cascade is such an intrinsic part of CSS that they put it right there in the name. If you’ve ever needed to use !important
to affect specificity in the cascade, you’ll know that it can be a tricky thing to deal with. In the early days of CSS, it was common to see highly specific selectors like this:
#sidebar ul li {}
We’re all much better at managing specificity nowadays. It’s a widely accepted best practice to keep specificity low and flat—to shun ID selectors, to make liberal use of classes, and to avoid unnecessary nesting. But there are still plenty of situations where a more specific selector will be useful. With the introduction of a newly proposed pseudo-class, more support of the shadow DOM, and the use of the all
property, we will soon be able to handle inheritance and specificity in new and exciting ways.
:is()
Pseudo-Class
The Lea Verou recently proposed this new pseudo-class specifically designed to control specificity. It’s already made its way to the CSS Level 4 Selectors spec. Lea has a write up of why it’s useful and there’s some coverage of it in the CSS-Tricks almanac.
Let’s take :not
as an example. The specificity of :not
is equal to the specificity of its argument. This makes using :not
rather painful. Take the following as an example:
We might expect that the .red
class would have higher specificity because it is lower in the cascade. However, for any styles to override div:not(.foobar)
they would need to at least match the specificity of a combined element selector (div
) and class selector (.foobar
). Another approach would be div.red
, but there is a better way. This is where :is
can help.
div:is(:not(.foobar)) {
background-color: black;
}
The :not
selector no longer adds any specificity, so the total specificity of the above selector is simply that of one element selector (div
). The .red
class would now be able to override it in the cascade. Once implemented, specificity hacks will be a thing of the past.
Shadow DOM
Today, many people are using classes in HTML like this:
<form class="site-search site-search--full">
<input type="text" class="site-search__field">
<button type="Submit" class="site-search__button">search</button>
</form>
When using shadow DOM, rather than following a verbose naming convention, we’ll be able to omit classes altogether. Styles defined within the shadow DOM are scoped to apply only within the component. Styling can be achieved with simple element selectors without worrying about whether the selectors will interfere with elements elsewhere on the page.
It’s liberating to write such easy CSS. No more effort spent naming things. Shadow DOM looks like it is finally making its way to full browser support. It’s likely to make it into the next release of Firefox while Edge have implementation as a high priority.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
53 | 63 | No | 79 | 10 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
117 | 117 | 117 | 11.0-11.2 |
all
Property
The The all
property is a way of setting all CSS properties at once—everything from align-content
to z-index
. What values does it accept? I can’t think of any use case when I’d want all properties to inherit
, but that’s an option. Then there’s initial
which is more like applying a CSS reset where all the styles are gone. No padding. No margin. The initial value is set per property, regardless of the element it is applied to. The initial value of display
is inline
, even if you apply it to a div. The font-style
of an em
tag is normal
, as is the font-weight
of a strong
tag. Link text will be black. You get the idea. (You can find the initial value of any CSS property on MDN.) This does perhaps limit its usefulness, going further than we might like by removing all styles, regardless of context.
Sadly, the most useful value for all
is also the least widely implemented: revert
. It can remove the styles that you as a developer have applied, while leaving the default user-agent styles intact. We’ve all seen a page of HTML without a stylesheet—black Times New Roman on a white (transparent) background with blue underlined links. If you really want to avoid inheritance, then all: revert
has you covered. All divs will be display: block
and spans will be inline
. All em
tags will be italic and strong
tags will be bold. Links will be blue and underlined.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
84 | 67 | No | 84 | 9.1 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
117 | 117 | 117 | 9.3 |
The future?
CSS-in-JS is a cry for help. We at @csswg should pay attention to this and address the issues before it gets worse.https://t.co/lWQ4ct61ir
— Lea Verou (@LeaVerou) May 24, 2017
The miscellany of rival unstandardized methods for writing CSS-in-JS was an attempt to sidestep these same issues. That approach has gained popularity over the last several years. Some of its proponents have deemed inheritance, the cascade and specificity as fundamentally flawed design decisions of the language. The CSS Working Group at the W3C is responding by improving the power of CSS and the native web platform. It will be interesting to see the outcome…
My understand of shadow dom is the only way to create a shadow root is via client side javascript? So if your JS doesn’t load you have an unstyled page? Seems like a pretty major flaw if so.
That’s absolutely true, and a very important consideration. Perhaps you have some components that rely on JavaScript for their functionality – their very raison d’etre relies on javascript. For these things shadow DOM is great. You certainly wouldn’t style every element on a page with shadow DOM – which perhaps I should have made more clear!
Actually this flaw (JS dependency) could be solved in future, as there is discussion about creating new tag,
shadowroot
→ https://github.com/whatwg/dom/issues/510As someone who still does things like:
#sidebar ul li {}
is there a recommended resource (or set of resources) all about modern CSS inheritance and best practices? I tend to work on small projects where I’m the only developer so I do what works for me but I’ve long wanted to level up with writing “good” CSS. Thanks.Hi Brad. My main advice: don’t bother worrying about naming conventions like BEM or things that sound fancy like OOCSS etc, however popular they may seem. Also don’t worry about Sass. Just make sure you understand how specificity works. Discussions about ‘CSS architecture’ are mostly bikeshedding (and there is no clear best way), but if you really want to read something:
https://adamwathan.me/css-utility-classes-and-separation-of-concerns
https://www.smashingmagazine.com/2016/11/css-inheritance-cascade-global-scope-new-old-worst-best-friends/
http://nicolasgallagher.com/about-html-semantics-front-end-architecture/
https://csswizardry.com/2012/05/keep-your-css-selectors-short/
Main advice: keep specificity as low as possible, use classes a bunch, don’t use IDs for CSS.
So that example could be
.sidebar li {} (which is easier to overwrite later on if need be without the need for !important)
Specificity matters a lot when you work in a team or when you come on to work done by another team. Its crucial you understand specificity and I’m not one for picking up and using whatever new fandangled naming convention is out right now however if you study up on ITCSS BEM (aka BEMIT) & CSS namespacing It will radically change the way you write CSS for the better. I was very much against BEM when I came across it, now I’m fully converted to it having seen how much issues specificity was causing us in work on large projects with a range of skill levels all pitching in over time. No system is perfect ITCSS and BEM certainly are not but their much better than traditional ways of working with CSS.
It will be a joy to use CSS on large projects. Follow people like Harry Roberts (CSSWIZARDRY). Oh and you’ll come across OOCSS, its a nonsense that doesn’t work in the real world, forget about it. OOCSS teaches us to be bone DRY which will cause all manner of issues down the line. Being just DRY enough is good enough….apply common sense.
:revert appears to only be supported by Safari at present.
Most of this seems quite useful to me, aside from the Shadow DOM, which for my coding just makes things more verbose. Aside from that, I will continue to make heavy use of id when writing CSS for SVGs. I tend to use the id as a sort of comment (in that an id of, say, ‘leaf’ will refer to the only leaf in the SVG) that doubles as a class of sorts. The real bonus in using an id in those cases is that the id, unlike an actual comment, is not removed during minification and uses fewer bytes, since id is two letters long and class is five letters long.
On the other hand, my HTML tends to be a bit more complex, so I avoid the use of id except in rare cases where it’s absolutely needed.
I don’t mind being a hybrid of the old and the new so long as I know I understand all the new if I ever need to make a sudden switch.