STRUCTURAL PSEUDO-CLASSES
Precision is a sign of mastery.
Sometimes we do not want to style all elements.
We may want to style only:
- the first element
- the last element
- every second element
- or all elements except one
Structural pseudo-classes help solve exactly these problems.
1. Neighboring Elements and Siblings
Structural pseudo-classes let us select specific elements from a group of child elements without adding extra classes.
Child elements in the same group are also called siblings.
Siblings are elements that have the same parent.
Example
<ul class="list">
<li class="list-item">
<a class="list-link">Link 1</a>
</li>
<li class="list-item">
<a class="list-link">Link 2</a>
</li>
<li class="list-item">
<a class="list-link">Link 3</a>
</li>
</ul>
In this example:
- the
li.list-itemelements are siblings - so structural pseudo-classes can be applied to them
The a.list-link elements are not selected as one sibling group here for this purpose, because each link is inside its own li.
So in practice, structural pseudo-classes are usually applied to the li elements here.
Diagram
ul
├── li.list-item
│ └── a.list-link
├── li.list-item
│ └── a.list-link
└── li.list-item
└── a.list-link
The li elements are siblings.
That is why they can be targeted with structural pseudo-classes.
2. :first-child and :last-child
The pseudo-classes :first-child and :last-child let us style only:
- the first element in a group of siblings
- the last element in a group of siblings
Example
<ul class="list">
<li class="list-item">HTML</li>
<li class="list-item">CSS</li>
<li class="list-item">JavaScript</li>
<li class="list-item">React</li>
</ul>
.list-item {
color: blue;
}
.list-item:first-child {
color: red;
}
.list-item:last-child {
color: green;
}
What Happens
- all items are blue by default
- the first item becomes red
- the last item becomes green
Diagram
1. HTML -> red
2. CSS -> blue
3. JavaScript -> blue
4. React -> green
3. Real Use Case: Removing Extra Margin
A very common layout problem is spacing between list items.
Example
.list-item {
margin-bottom: 12px;
}
Problem
- every item gets a bottom margin
- the last item also gets a bottom margin
- this creates unnecessary extra space below the list
Solution with :last-child
.list-item {
margin-bottom: 12px;
}
.list-item:last-child {
margin-bottom: 0;
}
Why This Matters
Outer spacing at the edges of a group should usually be removed.
Otherwise it may:
- create unexpected extra space
- visually enlarge the parent element
- make layout harder to control
Diagram
Without cleanup:
Item 1
margin
Item 2
margin
Item 3
margin <- unnecessary extra space
With :last-child:
Item 1
margin
Item 2
margin
Item 3
(no extra margin below)
4. :not() Pseudo-class
The :not() pseudo-class selects all elements that do not match a given condition.
Syntax
:not(selector)
The selector inside :not() should be a simple selector, for example:
- universal selector
- type selector
- id selector
- attribute selector
- class selector
- pseudo-class
Main Idea
:not() means "select everything except this."
5. Using :not() for Margins
Instead of:
- giving margin to all items
- then removing it from the last one
we can write one cleaner rule:
.list-item:not(:last-child) {
margin-bottom: 20px;
}
How to Read It
Apply styles to all .list-item elements that are not the last child.
So this selector styles:
- the first item
- middle items
- but not the last item
Why This Is Often Better
- one rule does the whole job
- less chance of forgetting a reset
- easier to read in many cases
Diagram
.list-item:not(:last-child)
✓ first item
✓ middle items
✗ last item
Extra Example
.button:not(.active) {
opacity: 0.6;
}
Meaning
style all buttons except the active one.
6. :nth-child()
The pseudo-class :nth-child(an + b) selects elements by number inside a sibling group.
It uses a formula or pattern.
Syntax
:nth-child(an + b)
Where
a= cycle stepn= counter starting from 0b= offset
Main Idea
:nth-child() lets us select elements by position.
7. Example Formula: 2n + 1
If:
a = 2b = 1
then the formula is:
2n + 1
This selects all odd-numbered elements.
Calculation
n = 0 -> 2*0 + 1 = 1
n = 1 -> 2*1 + 1 = 3
n = 2 -> 2*2 + 1 = 5
n = 3 -> 2*3 + 1 = 7
...
So it selects:
- 1
- 3
- 5
- 7
- and so on
8. Practical Use of :nth-child()
In practice, :nth-child() is often used for:
- even elements
- odd elements
- striped table rows
- alternating list backgrounds
- repeating layout patterns
Example List
<ul class="list">
<li class="list-item">1</li>
<li class="list-item">2</li>
<li class="list-item">3</li>
<li class="list-item">4</li>
<li class="list-item">5</li>
<li class="list-item">6</li>
<li class="list-item">7</li>
<li class="list-item">8</li>
<li class="list-item">9</li>
<li class="list-item">10</li>
</ul>
9. Selecting Even Elements
To select all even elements:
.list-item:nth-child(2n) {
background-color: orange;
}
or:
.list-item:nth-child(even) {
background-color: orange;
}
These two forms mean the same thing.
Selected Items
2, 4, 6, 8, 10
Diagram
1 -> normal
2 -> orange
3 -> normal
4 -> orange
5 -> normal
6 -> orange
7 -> normal
8 -> orange
9 -> normal
10 -> orange
10. Selecting Odd Elements
To select all odd elements:
.list-item:nth-child(2n + 1) {
background-color: orange;
}
or:
.list-item:nth-child(odd) {
background-color: orange;
}
These two forms also mean the same thing.
Selected Items
1, 3, 5, 7, 9
Diagram
1 -> orange
2 -> normal
3 -> orange
4 -> normal
5 -> orange
6 -> normal
7 -> orange
8 -> normal
9 -> orange
10 -> normal
11. Quick Comparison
:first-child
Selects the first sibling element.
Example:
.list-item:first-child {
color: red;
}
:last-child
Selects the last sibling element.
Example:
.list-item:last-child {
margin-bottom: 0;
}
:not()
Selects everything except what is inside the parentheses.
Example:
.list-item:not(:last-child) {
margin-bottom: 20px;
}
:nth-child()
Selects elements by position or pattern.
Example:
.list-item:nth-child(even) {
background-color: orange;
}
12. Very Short Practical Examples
First Item
.list-item:first-child {
font-weight: bold;
}
Meaning
make only the first item bold.
Last Item
.list-item:last-child {
border-bottom: none;
}
Meaning
remove the bottom border from the last item.
All Except the Last
.list-item:not(:last-child) {
margin-bottom: 16px;
}
Meaning
add spacing between items, but not after the last one.
All Even Items
.list-item:nth-child(even) {
background: #f0f0f0;
}
Meaning
every second item gets a different background.
All Odd Items
.list-item:nth-child(odd) {
background: #ddd;
}
Meaning
the 1st, 3rd, 5th, and so on get a different background.
13. Full Simple Example
HTML
<ul class="list">
<li class="list-item">HTML</li>
<li class="list-item">CSS</li>
<li class="list-item">JavaScript</li>
<li class="list-item">React</li>
</ul>
CSS
.list-item {
padding: 8px;
}
.list-item:first-child {
color: red;
}
.list-item:last-child {
color: green;
}
.list-item:not(:last-child) {
margin-bottom: 10px;
}
.list-item:nth-child(even) {
background-color: #f2f2f2;
}
What Happens Here
- all items get padding
- the first item becomes red
- the last item becomes green
- all items except the last get bottom margin
- even items get a light background
14. Common Mistakes
Mistake 1
Using structural pseudo-classes on elements that are not siblings in the same group.
Mistake 2
Forgetting that :first-child and :last-child depend on position inside the parent.
Mistake 3
Using margin-bottom on all items and forgetting to remove it from the last one.
Mistake 4
Confusing odd and even.
Remember:
odd= 1st, 3rd, 5th...even= 2nd, 4th, 6th...
Mistake 5
Thinking :not() only works with classes.
It can also be used with pseudo-classes and other simple selectors.
15. Short Cheat Sheet
:first-child -> first element
:last-child -> last element
:not(...) -> everything except ...
:nth-child() -> element by number or pattern
16. Final Summary
Structural pseudo-classes help us style elements based on their position among siblings.
They are useful when we need precision without adding extra classes.
The most common ones are:
:first-child:last-child:not():nth-child()
They are especially useful for:
- spacing between items
- special styling for first or last elements
- alternating row colors
- creating exceptions inside a group of elements
Super Short Memory Line
first-child = first, last-child = last, not() = except, nth-child() = by number