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.
