Structural Pseudo-Classes (:nth-child, :first-of-type)
Structural pseudo-classes select elements based on their POSITION in the document structure — first, last, only, or nth in some sequence — rather than by class, attribute, or state. They let you style "the first item in this list" or "every row except the last" directly in CSS, without adding a special class to that one element in your markup.
The basics
:first-child — matches an element only if it is the very first child of its parent, regardless of element type.
:last-child — matches an element only if it is the very last child of its parent.
:first-of-type — matches an element if it is the first child of its specific element TYPE among its siblings (other types are ignored when counting).
:last-of-type — the last child of its specific element type among its siblings.
:only-child — matches an element that is the one and only child of its parent (no siblings at all).
:first-child vs. :first-of-type — a common source of confusion
:first-child | :first-of-type |
|---|---|
Must be the first child of ANY type in its parent | Must be the first child of its OWN element type — other types before it don't disqualify it |
In mixed content, only matches if that exact element happens to be first overall | Matches the first occurrence of that tag, even if other tags come before it |
Worked example — the difference, concretely
<article> <span>Byline</span> <p>First paragraph.</p> <p>Second paragraph.</p> </article>
/* p:first-child — matches NOTHING here.
The <p> is not the first child overall; <span> is. */
article p:first-child {
font-weight: bold;
}
/* p:first-of-type — matches "First paragraph."
It IS the first <p> among the <p> elements, even though
a <span> came before it in the markup. */
article p:first-of-type {
font-weight: bold;
}This is exactly the trap that catches people: if you want to style "the first paragraph" in content that might have other elements (a byline, a figure, a heading) mixed in before it, :first-child silently matches nothing the moment something else precedes it. :first-of-type is almost always the one you actually want for "the first element of this tag", while :first-child is for "the first thing in this container, whatever it is".
:only-child and :last-of-type in practice
/* Style a card differently if it's the only item in its grid */
.grid-item:only-child {
max-width: 400px;
margin: 0 auto;
}
/* Remove the bottom border from the last paragraph of an article,
even if a <footer> or <aside> follows it in the markup */
article p:last-of-type {
border-bottom: none;
}