:has(), :not() & :nth-child()

Smarter selectors

Lesson 5 of 20 · 11 min · Module 2: Selectors and the cascade

Some styles depend on context: highlight the plan whose radio button is checked, stripe every other row, draw a line between items but not after the last one. Modern selectors express these rules directly, without extra classes or JavaScript.

Start this lesson →

Free. No installation: you write and run the code in your browser, and it is checked automatically.

By the end, you’ll be able to…

  • Style an element based on what it contains with :has()
  • Select by position with :nth-child()
  • Exclude elements with :not()

The idea

.plan:has(input:checked) selects a plan that contains a checked input, the long-awaited "parent selector". :nth-child(even) selects every second item, and :not(:last-child) selects every item except the last. :is() and :where() group selectors; :where() adds no specificity at all.

/* a card that contains an image */
.card:has(img) { padding-top: 0; }

/* every h2 and h3 inside an article */
article :is(h2, h3) { color: #2f6f4e; }

01A parent that reacts to its children

.plan:has(input:checked) matches the label whose radio button is checked. Click another plan in the preview and the highlight follows, with no JavaScript involved.

02Counting children

nth-child(even) matches the 2nd, 4th, 6th… child. nth-child(3n+1) matches the 1st, 4th, 7th…, and nth-child(-n+3) the first three.

li:nth-child(odd)    /* 1, 3, 5 … */
li:nth-child(3n)     /* 3, 6, 9 … */
li:nth-child(-n+3)   /* the first three */

03Specificity of :is() and :where()

article :is(h2, .title) takes the specificity of its most specific argument. :where() always counts as zero, which makes it ideal for defaults that should be easy to override.

Your turn

1) Give the plan that contains the checked radio button border-color: #2f6f4e and background: #eef6e5, using .plan:has(input:checked). 2) Give every even .features li the background #f6f8f2. 3) Give every feature row except the last one border-bottom: 1px solid #e3ead7.

What the checks look for

  • The checked plan has a green border
  • The checked plan has a light green background
  • The other plans keep the neutral border
  • Even rows are striped
  • Odd rows stay plain
  • Rows are separated by a line
  • The last row has no line
Try it in the editor →

Take this with you

:has(), :nth-child() and :not() replace many helper classes.

Go a little further

Use .features:has(li:nth-child(5)) to switch the list to two columns as soon as it has five or more items.

Learn HTML & CSS Intermediate for free

20 short lessons with real code and instant checks.

See the whole course →