Les pseudo-classes CSS permettent de sélectionner des éléments selon leur position parmi leurs frères et sœurs, ou selon leur type. Ce guide résume les sélecteurs relationnels (enfant/descendant) puis détaille :first-child, :last-child, :nth-child(), :nth-last-child(), :first-of-type, :last-of-type, :nth-of-type() et leurs pièges courants.
Sélecteurs relationnels (rappel)
Enfants directs
.parent > * {
/* Tous les enfants directs de .parent */
border: 1px solid #000;
margin: 6px;
}Tous les descendants (enfants + petits-enfants, etc.)
.parent * {
/* Tous les descendants de .parent */
color: blue;
}* > p sélectionne chaque <p> qui est enfant direct de n’importe quel parent… ce qui équivaut pratiquement à p. Préférez p ou un parent précis (.parent > p).
Règles de comptage (très important)
Le comptage commence à 1 (pas à 0).
:nth-child() et :nth-last-child() comptent tous les enfants, peu importe la balise.
Les variantes of-type (ex. :nth-of-type()) comptent seulement les frères du même type de balise.
Mots-clés :
odd≡2n+1→ impairs (1, 3, 5…)even≡2n→ pairs (2, 4, 6…)
Premier / dernier enfant :first-child et :last-child
/* 1er et dernier enfant DIRECT d’une liste */
.conteneur ul > :first-child { font-weight: 700; }
.conteneur ul > :last-child { margin-bottom: 20px; }
/* 1er et dernier <p> ENFANT DIRECT d’un <div> */
div > p:first-child { color: #333; }
div > p:last-child { color: #666; }
.conteneur ul:first-child cible un <ul> qui est premier enfant de son parent, pas le premier <li> de la liste.
Position par rang : :nth-child() & :nth-last-child()
Exemples simples
/* 3e enfant (quel que soit le type) */
ul li:nth-child(3) { background: #f7f7f7; }
/* Pairs/impairs parmi tous les enfants */
div > p:nth-child(even) { background: #eee; } /* 2,4,6… */
div > p:nth-child(odd) { background: #f9f9f9; } /* 1,3,5… */Depuis la fin
/* Pairs en partant de la fin, tous types confondus */
ul li:nth-last-child(even) { color: #444; }Motifs arithmétiques
/* 1, 11, 21, 31, … (tous les enfants) */
div > *:nth-child(10n + 1) { outline: 1px dashed #999; }Si vous voulez “chaque 10e <p>”, utilisez plutôt p:nth-of-type(10n+1) (voir section suivante) pour ne pas être perturbé par d’autres balises au même niveau.
Position par type : :first-of-type, :last-of-type, :nth-of-type(), :nth-last-of-type()
Syntaxes correctes (sans parenthèses pour first/last)
/* Premier et dernier élément de CHAQUE type parmi les enfants directs */
.parent > *:first-of-type { border-top: 2px solid #000; }
.parent > *:last-of-type { border-bottom: 2px solid #000; }
/* Chaque 2e paragraphe (2, 4, 6…) */
.parent > p:nth-of-type(2n) { color: blue; }
/* Spans impairs (1,3,5…) */
.parent > span:nth-of-type(odd) { color: red; }
/* Pair en partant de la fin, parmi les <li> uniquement */
ul > li:nth-last-of-type(even) { font-style: italic; }Différence clé :
:nth-child(3)= 3e enfant, quel que soit son type.p:nth-of-type(3)= 3e<p>(ignore les autres balises).
Exemples pratiques (ciblages utiles)
Le 2e paragraphe seulement si c’est bien un <p>
.article > p:nth-of-type(2) { font-size: 1.05rem; }
Tous les 5e éléments d’une grille… mais seulement les <li>
.grid > li:nth-of-type(5n) { transform: scale(1.02); }Au survol du conteneur, styliser son 2e <p>
.card:hover > p:nth-of-type(2) { color: #0a84ff; }N’appliquer le style qu’aux <li> pairs visibles
/* Bonus : éviter certains éléments via :not() si besoin */
ul > li:not(.is-hidden):nth-of-type(even) { background: #f2f2f2; }Mémo comparatif
| Pseudo-classe | Compte quoi ? | Exemple | Sélectionne… |
|---|---|---|---|
:first-child / :last-child | Tous les enfants | div > p:first-child | le <p> si c’est le 1er enfant du div |
:nth-child(an+b) | Tous les enfants | li:nth-child(odd) | les éléments 1,3,5… |
:nth-last-child(an+b) | Tous les enfants (depuis la fin) | li:nth-last-child(2) | l’avant-dernier élément |
:first-of-type / :last-of-type | Mêmes balises uniquement | p:first-of-type | le 1er <p> |
:nth-of-type(an+b) | Mêmes balises uniquement | p:nth-of-type(3) | le 3e <p> |
:nth-last-of-type(an+b) | Mêmes balises (depuis la fin) | p:nth-last-of-type(2) | l’avant-dernier <p> |
Pièges fréquents (et comment les éviter)
- Pairs/impairs inversés :
odd= impairs,even= pairs. - Mauvais parent ciblé :
ul:first-child≠ “1erli”. Utilisezul > li:first-child. - Type mélangé : si des balises différentes cohabitent,
:nth-child()peut surprendre. Préférez:nth-of-type()quand vous visez un type précis. - Espaces mal placés :
.conteneur ul :last-child(avec espace) cible le dernier enfant de n’importe quel descendant deul. Pour le dernier li, utilisezul > li:last-child. - Parenthèses :
:first-of-typeet:last-of-typen’ont pas de parenthèses. - Commentaires CSS : n’oubliez pas de fermer
/* … */.
Exercice : petit HTML de test + styles (copiez, testez)
<div class="parent">
<p>Paragraphe 1</p>
<span>Span 1</span>
<p>Paragraphe 2</p>
<div>
<p>Paragraphe imbriqué</p>
</div>
</div>
<ul class="liste">
<li>Un</li>
<li>Deux</li>
<li>Trois</li>
<li>Quatre</li>
<li>Cinq</li>
</ul>
/* Enfants directs & descendants */
.parent > * { border: 1px solid #000; margin: 6px; }
.parent * { color: #222; }
/* Premier/dernier li */
.liste > :first-child { font-weight: 700; }
.liste > :last-child { color: #0a84ff; }
/* Impairs/pairs (rang, tous types) */
.liste > li:nth-child(odd) { background: #fafafa; }
.liste > li:nth-child(even) { background: #f0f0f0; }
/* 2e paragraphe uniquement (par type) */
.parent > p:nth-of-type(2) { background: #ffe; }
/* Pattern 10n+1 sur les <li> uniquement */
.liste > li:nth-of-type(10n+1) { outline: 1px dashed #888; }En bref
- Choisissez
:nth-child()quand la position absolue parmi tous les enfants vous suffit. - Préférez
:nth-of-type()pour cibler la nième balise d’un type précis. - Vérifiez toujours le parent et la structure du DOM : le comptage dépend du voisinage réel.
Au-delà de la syntaxe : l’art du CSS structuré
Maîtriser réellement les CSS demande de dépasser la simple compréhension des propriétés pour adopter une architecture robuste. Ce qu’une formation courte ne vous apporte pas toujours, c’est cette rigueur indispensable pour maintenir des projets à grande échelle. Pour aller plus loin et structurer durablement vos interfaces, je vous invite à consulter mon article complet sur la méthode pour nommer correctement ses classes CSS avec BEM.
