Not to mention

Extra

Manuel Matuzović wrote about the difference between :has(:not()) and :not(:has()) almost two years ago.

Another thing that may trip you up when using :not() is going the opposite direction: While :has() is about finding out about child nodes, what about selecting something further up in the DOM? Consider the following example.

:not(.specific) .element {
	color: tomato;
}

Here we are changing the color of an .element that does not have a .specific ancestor. Well, changes are this is always the case. Unless every ancestor all the way up to <HTML> has the .specific class attached, our color will be applied.

For :not() to make sense, we need to tie to something else. Sadly this requires us to have .something to work with in the first place.

.something:not(.specific) .element {
	color: tomato;
}

Now we target an .element that is part of .something, unless said something is .specific.

So we are done?

Almost. There is a way to make the wildcard version actually useful.

:not(.specific) > .element {
	color: tomato;
}

By adding the child combinator, we are not targeting any ancestor, but just the parent. While this obviously a bit limiting, targeting a parent that is not .specific may at times be exactly what you need.


Update

Now we are done. Or so I thought. But Christopher Kirk-Nielsen reached out to inform me that by putting the ancestor inside :not() we can get rid of the limitation. You can check out his article for all the details, I’ll just put the example below.

.element:not(.specific *) {
	color: tomato;
}