You will select elements by their position in the DOM using structural pseudo-classes such as :nth-child(), :first-of-type, and :only-child.
What it is
Structural pseudo-classes match elements based on where they sit among their siblings, not on their class, id, or content. They are useful when the markup is generic and you want styling to follow document structure.
Think of the DOM as a family tree. A parent has children in order. :nth-child() asks, “Is this element the nth child of its parent?” while :nth-of-type() asks, “Is this element the nth child of its parent that has the same tag name?” Related selectors include :first-child, :last-child, :first-of-type, :last-of-type, :only-child, and :only-of-type.
Why it matters
- It creates zebra-striped tables or lists without adding classes to every row.
- It removes unwanted spacing from the first or last element in a container.
- It highlights the first item in a menu or the only item in a list.
- It keeps CSS shorter when structure, not semantics, drives the design.
- It helps style generated content where you cannot easily edit the HTML.
Syntax or steps
The smallest useful pattern is a selector followed by a structural pseudo-class:
parent > child:nth-child(even) {
background: #f4f4f4;
}
For :nth-child(), you can use keywords such as odd and even, or a formula like 2n + 1. For type-based selectors, place the pseudo-class after the element name:
p:first-of-type {
margin-top: 0;
}
Use :only-child when an element has no siblings at all, and :only-of-type when it is the only sibling with the same tag name.
Example
<ul class="menu">
<li>Home</li>
<li>Docs</li>
<li>Blog</li>
<li>Contact</li>
</ul>
<ul class="single">
<li>Only item</li>
</ul>
<style>
.menu li:nth-child(even) {
background: #f4f4f4;
}
.menu li:first-of-type {
font-weight: bold;
}
.single li:only-child {
border: 2px solid #333;
}
</style>
Part by part: .menu li:nth-child(even) selects every second li inside the menu, producing alternating backgrounds. .menu li:first-of-type selects the first li among its siblings, making “Home” bold. .single li:only-child selects the li only if it has no siblings, so the single-item list gets a border.
Common mistakes
- Confusing
:nth-child()with:nth-of-type(). If a parent contains adivand then twopelements,p:nth-child(2)selects the firstpbecause it is the second child overall, whilep:nth-of-type(2)selects the secondp. - Assuming
:first-of-typeis always the first child. It is the first element of that tag name, even if other element types appear before it. - Using
:last-childto remove bottom margin. If the last child is not the element you want, use:last-of-typeinstead. - Using
:only-childwhen you mean:only-of-type.:only-childfails if there are siblings of a different tag;:only-of-typeonly cares about siblings with the same tag.
When to use it
Structural pseudo-classes are best when position in the tree is the real reason for styling. If the design depends on meaning or explicit intent, a class is safer.
| Approach | Use when | Avoid when |
|---|---|---|
:nth-child() | Alternating rows or items in a uniform list. | Children may be mixed element types. |
:first-of-type / :last-of-type | Spacing the first or last paragraph, heading, or list item. | You need the first or last child regardless of tag. |
| Utility classes | Design intent must survive markup changes. | You want to avoid extra HTML. |
Practice
Guided exercise: Style a list so even items have a light background, the first item is bold, and a list with one item has a border. Use li:nth-child(even), li:first-of-type, and li:only-child.
Challenge: Remove the top margin from the first paragraph in an article and the bottom margin from the last paragraph, even if the article contains headings or images. Hint: use p:first-of-type and p:last-of-type.
Quick check
Question: What does p:nth-child(2) select?
Answer: A p element that is the second child of its parent, not necessarily the second paragraph among its siblings.
Summary
Structural pseudo-classes let CSS respond to an element’s position in the DOM. Choose child-based selectors when overall sibling order matters, and type-based selectors when the element’s tag name matters.