Kodokon kodokon.com

Modern selectors: :has, :is, :where, advanced :nth-child

Leverage the recent functional pseudo-classes to select based on content, group selectors without inflating specificity, and target complex patterns.

9 min · 3 questions

Open this lesson in Kodokon

:is() and :where() both group selectors, but they diverge on one decisive point: specificity. :is(nav, .menu, #main a) takes the specificity of its strongest argument (here the ID), even if nav is the one that matches. :where() has a specificity of zero, whatever its arguments. Practical consequence: :where() is the ideal tool for resets and a design system's default styles, which any simple class selector can then override without a fight.

CSS
:where(h1, h2, h3, h4) {
  margin-block: 0;
  line-height: 1.2;
}

:is(article, aside, section) :is(h2, h3) {
  font-family: var(--font-heading);
}

.prose :is(ul, ol) li + li {
  margin-top: 0.5em;
}
Grouping and controlling specificity

:has() finally reverses the reading direction: the selector targets the host element, not what it contains. .card:has(> img) selects the card that has an image as a direct child. Combined with state pseudo-classes, it lets information flow upwards without JavaScript: styling a label when its field is invalid, a form when a field has focus, a table row when its checkbox is checked.

CSS
.field:has(input:invalid:not(:focus)) label {
  color: #dc2626;
}

.card:has(> img) {
  padding-top: 0;
}

tr:has(input[type="checkbox"]:checked) {
  background: #eef2ff;
}
Selecting based on content or internal state

On :nth-child(), two skills to consolidate. First, reading An+B formulas: 2n (even), 2n+1 (odd), -n+3 (the first three), n+4 (from the fourth to the end); the range "from the 2nd to the 5th" is written by combining :nth-child(n+2):nth-child(-n+5). Second, the more recent of S syntax: :nth-child(2n of .visible) counts only among the children that match .visible, whereas .visible:nth-child(2n) counts all children and then filters - two very different results as soon as other elements are interleaved.

CSS
tr:nth-child(odd) {
  background: #fafafa;
}

li:nth-child(-n + 3) {
  font-weight: 600;
}

li:nth-child(n + 2):nth-child(-n + 5) {
  border-top: 1px solid #e5e7eb;
}

.row:nth-child(2n of .visible) {
  background: #eef2ff;
}
An+B formulas and the of S syntax

Knowledge check

Make sure you remember the key points of this lesson.

  1. What is the specificity of :where(#main, .nav, a)?
    • That of the ID, i.e. (1,0,0)
    • The sum of the three arguments
    • Zero, whatever the arguments
    • That of the argument that actually matches
  2. What does .card:has(> img) select?
    • Images that are direct children of a card
    • Cards that contain an image as a direct child
    • Cards located after an image in the DOM
    • All images that are descendants of cards
  3. In a list where only some li elements carry .visible, what does li:nth-child(2n of .visible) do?
    • It targets the even li elements of the list that also have the .visible class
    • It targets every other li, counting only the li.visible elements
    • It targets the even li elements and then adds the .visible class to them