These four make long selectors shorter and let you express things that used to be impossible.
:not(): everything except
<style>
li:not(.done) { font-weight: bold; }
li.done { color: #888; text-decoration: line-through; }
</style>
<ul><li>Write the post</li><li class="done">Pick a title</li><li>Add images</li></ul>
li:not(.done) matches list items that do not have the class done.
:is(): a shorter list of alternatives
These two are equal:
header a:hover, nav a:hover, footer a:hover { color: #d94a00; }
:is(header, nav, footer) a:hover { color: #d94a00; }
:where(): the same, with zero strength
:where() works like :is() but adds no specificity, so rules written with it are easy to override. It is excellent for base styles and resets. See CSS Specificity.
:where(h1, h2, h3) { margin-block: 0.5em; }
:has(): select a parent by its children
:has() is called “the parent selector”. It matches an element that contains something.
<style>
.card { border: 1px solid #ddd; padding: 10px; margin-bottom: 8px; border-radius: 8px; }
.card:has(.pic) { background: #fdf2e9; }
.card:has(input:checked) { border-color: #1a5276; background: #f4f9fc; }
.pic { display: inline-block; width: 30px; height: 30px; background: #d94a00; vertical-align: middle; }
</style>
<div class="card"><span class="pic"></span> A card with a picture</div>
<div class="card">A text-only card</div>
<div class="card"><label><input type="checkbox"> Select this card</label></div>
Tick the checkbox and the whole card changes. Without :has() that needed JavaScript.
Support
:is(), :where() and :not() work everywhere. :has() is supported by all current major browsers, but check Can I use if you have to support older ones.