π― CSS Structural Pseudo-classes: Finding Elements Like a Detective
Imagine youβre a detective at a big family gathering. Everyone is sitting around a huge dinner table. Your job? Find specific people based on where they sit and who they are. Thatβs exactly what structural pseudo-classes do in CSS!
π The Family Analogy
Think of HTML elements as family members sitting at a table:
- Parent = The table (container)
- Children = People sitting at the table
- First child = Person at the head of the table
- Last child = Person at the end
Structural pseudo-classes help you pick exactly who you want to style!
πΆ Child Selector Pseudo-classes
These find elements based on their position among siblings.
:first-child β The Firstborn
Selects an element that is the first child of its parent.
p:first-child {
color: gold;
font-weight: bold;
}
Real Talk: If <p> is the very first thing inside its parent, it gets styled!
:last-child β The Baby of the Family
Selects the last child of a parent.
li:last-child {
border-bottom: none;
}
Pro Tip: Great for removing borders on the last list item!
:nth-child(n) β The Magic Counter
Pick any child by number! Like saying βgive me the 3rd kid.β
/* Every 2nd child (even items) */
tr:nth-child(2n) {
background: #f0f0f0;
}
/* First 3 children */
li:nth-child(-n+3) {
font-weight: bold;
}
/* Every 3rd child starting from 2nd */
div:nth-child(3n+2) {
color: blue;
}
The Formula:
2n= even (2, 4, 6β¦)2n+1= odd (1, 3, 5β¦)3n= every third (3, 6, 9β¦)-n+3= first three only
:nth-last-child(n) β Counting Backwards
Same as nth-child, but starts from the end!
/* Last 2 items */
li:nth-last-child(-n+2) {
opacity: 0.5;
}
π¨ Type Selector Pseudo-classes
These are pickier! They only count elements of the same type.
:first-of-type β First of Its Kind
Selects the first element of that type among siblings.
p:first-of-type {
font-size: 1.2em;
}
The Difference:
:first-child= Must be THE first child (any type):first-of-type= First<p>even if other things come before it
<div>
<h2>Title</h2>
<p>I'm first-of-type!</p>
<p>I'm second</p>
</div>
Here, the <p> is NOT :first-child (h2 is first), but it IS :first-of-type!
:last-of-type β Last of Its Kind
p:last-of-type {
margin-bottom: 0;
}
:nth-of-type(n) β Counting Only Your Type
/* Style every 2nd paragraph */
p:nth-of-type(2n) {
background: #fffde7;
}
Only counts <p> elements, ignores everything else!
:nth-last-of-type(n) β Backwards Type Counting
/* Second-to-last image */
img:nth-last-of-type(2) {
border: 3px solid red;
}
π Only-Element Pseudo-class
:only-child β The Only Kid
Matches when an element is the only child of its parent.
p:only-child {
text-align: center;
font-style: italic;
}
<div>
<p>I'm styled! I'm alone here.</p>
</div>
<div>
<p>I'm NOT styled</p>
<p>Because I have a sibling</p>
</div>
:only-of-type β Only One of Your Kind
An element is the only one of its type among siblings.
img:only-of-type {
display: block;
margin: 0 auto;
}
Even if there are other elements, if thereβs only ONE image, it matches!
π The :empty Pseudo-class
Selects elements with NO children at all β not even text or spaces!
.message:empty {
display: none;
}
.box:empty::before {
content: "Nothing here!";
color: gray;
}
Watch Out! Even a single space breaks :empty:
<div></div> β
Matches :empty
<div> </div> β Has whitespace
<div>Hi</div> β Has text
π³ The :root Pseudo-class
Selects the root element of the document. In HTML, thatβs always <html>.
:root {
--primary-color: #667eea;
--spacing: 16px;
font-size: 16px;
}
Why Use It?
- Define CSS custom properties (variables)
- Set global styles
- Higher specificity than
htmlselector
/* Using variables */
button {
background: var(--primary-color);
padding: var(--spacing);
}
π― The :target Pseudo-class
Matches an element when its ID matches the URL hash.
:target {
background: yellow;
animation: highlight 1s ease;
}
@keyframes highlight {
from { background: orange; }
to { background: yellow; }
}
How It Works:
- URL:
page.html#section2 - Element:
<section id="section2"> - That section now matches
:target!
<nav>
<a href="#intro">Intro</a>
<a href="#details">Details</a>
</nav>
<section id="intro">...</section>
<section id="details">...</section>
Click a link β URL changes β Section highlights!
Cool Uses:
- Highlight sections when navigated
- CSS-only tabs (no JavaScript!)
- Show/hide content
πΊοΈ Quick Reference Map
graph LR A["Structural Pseudo-classes"] --> B["Child-based"] A --> C["Type-based"] A --> D["Special"] B --> B1[":first-child"] B --> B2[":last-child"] B --> B3[":nth-child"] B --> B4[":only-child"] C --> C1[":first-of-type"] C --> C2[":last-of-type"] C --> C3[":nth-of-type"] C --> C4[":only-of-type"] D --> D1[":empty"] D --> D2[":root"] D --> D3[":target"]
π‘ Key Differences to Remember
| Scenario | :first-child |
:first-of-type |
|---|---|---|
<div><p>Hi</p></div> |
β Matches | β Matches |
<div><h1>...</h1><p>Hi</p></div> |
β No match | β Matches |
Simple Rule:
- Child selectors count ALL siblings
- Type selectors count only SAME TYPE siblings
π You Did It!
Now you can:
- β Select the first, last, or any numbered child
- β Target elements by type position
- β
Style lonely elements with
:only-child - β
Handle empty containers with
:empty - β
Set global variables with
:root - β
Create dynamic highlights with
:target
Youβre now a CSS detective! Go find those elements! π
