Pseudo-classes CSS pour cibler des enfants

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 :

  • odd2n+1impairs (1, 3, 5…)
  • even2npairs (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-classeCompte quoi ?ExempleSélectionne…
:first-child / :last-childTous les enfantsdiv > p:first-childle <p> si c’est le 1er enfant du div
:nth-child(an+b)Tous les enfantsli: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-typeMêmes balises uniquementp:first-of-typele 1er <p>
:nth-of-type(an+b)Mêmes balises uniquementp: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 ≠ “1er li”. Utilisez ul > 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 de ul. Pour le dernier li, utilisez ul > li:last-child.
  • Parenthèses : :first-of-type et :last-of-type n’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.

Stéphane Arrami

Stéphane Arrami

Architecte de l'information, développeur et formateur web, IA et NoCode depuis 2017, je pilote mes propres structures (formateur-web.com et wanimi.com pour l'Info-Com) tout en intervenant pour OpenClassrooms.

Je suis également membre de jury officiel pour les titres professionnels de développeur web et designer UI.

Retour en haut