Child and Structural Selectors

Structural pseudo-classes select by position among siblings. In An+B, n runs 0, 1, 2..., so 2n+1 gives 1, 3, 5 and -n+3 the first three. All weigh 0-1-0; :nth-child(An+B of S) also adds the weight of S.

Structural pseudo-classes
Selector Matches
:first-child / :last-child / :only-child First / last / sole child of its parent
tr:nth-child(odd) / :nth-last-child(2) Rows 1, 3, 5... / second from the end
li:nth-child(2 of .done) The second child that has class done
p:first-of-type / p:nth-of-type(3) First / third <p>, ignoring other tags
p:empty A <p> with no children, not even whitespace

p:first-child needs a <p> that is literally first; p:first-of-type skips a leading <h3>. The of S form (Baseline widely available since November 2025) counts only siblings matching S, so stripes survive hidden rows.

Stripes that skip hidden items, -of-type and :emptyHTMLLive
<!doctype html>
<style>
  body { font: 15px system-ui; }  p { margin: 0; }
  ul { display: flex; gap: 4px; list-style: none; padding: 0; margin: 0 0 8px; }
  li { padding: 2px 10px; }  li:last-child { box-shadow: inset 0 -3px #1565c0; }
  li:nth-child(odd of :not([hidden])) { background: #e3f2fd; }
  div p:first-of-type { font-weight: 700; }  div :empty { height: 6px; background: #ef6c00; }
</style>
<ul><li>One</li><li hidden>Hidden</li><li>Two</li><li>Three</li><li>Four</li></ul>
<div><b>Notes:</b><p>First p, bold via :first-of-type</p><p></p></div>
Browser output of Listing 4.16
Browser output of 16