:is() and :where()
The choice between two selectors that shorten the same lists comes down to whether the rule should keep its weight.
These two get introduced together and the examples never tell them apart. So here’s the one difference that actually matters.
/* both of these do the same matching */
:is(h1, h2, h3) a { }
:where(h1, h2, h3) a { }
:is() takes the specificity of its heaviest argument. :where() always has a specificity of zero.
So :is(h1, #masthead) a weighs as much as an id selector, even when it matched the h1. That surprises people exactly once, usually while they’re wondering why a perfectly ordinary two-class rule underneath it keeps losing.
I think :where() is the useful one because it weighs nothing. These days I reach for it every time I write a defaults layer.
Anything I put in a reset or a base stylesheet goes inside :where(), so it applies but never fights. A single class anywhere later beats it without a !important and without anybody thinking about it.
:where(ul, ol) {
margin-block: 0;
padding-inline-start: 0;
}
Both also give you forgiving parsing. In a plain selector list, one selector the browser doesn’t understand takes the whole rule down with it. Inside either of these, the browser just skips that one and the rest still work. That’s why they turn up in cross-browser workarounds.
I’ve ended up with a rule, such as it is. Anything meant to be overridden gets :where(), and anything meant to hold gets :is().