Imbrication

L'imbrication des règles de style CSS peut rendre vos feuilles de style plus organisées, plus faciles à lire et plus faciles à gérer.

Présentation

Maintenant que vous avez découvert les sélecteurs, vous vous demandez probablement comment mieux les organiser dans vos feuilles de style. Imaginez que vous appliquiez des styles à des éléments dans une section "fonctionnalité" de votre site. Avec l'imbrication, vous pouvez regrouper ces styles à l'intérieur de la règle .feature comme suit :

.feature {
  button {
    color: blue;
  }

  .link {
    color: red;
  }

  .text {
    font-size: 1.3em;
  }
}

Cela revient à écrire chaque style séparément :

.feature button {
  color: blue;
}

.feature .link {
   color: red;
}

.feature .text {
   font-size: 1.3em;
}

Vous pouvez imbriquer autant de niveaux que nécessaire.

.feature {
  .heading {
    color: blue;

    a {
      color: green;
    }
  }
}

Regrouper et établir des relations

L'imbrication vous permet de regrouper et d'établir des relations entre les règles de style de manière plus concise.

Par défaut, la règle imbriquée sera liée à la règle externe en tant que combinateur descendant. Utilisez les sélecteurs des règles imbriquées pour modifier les relations.

/* targets headings that are siblings of the .feature element and come immediately after it */
.feature {
  + .heading {
    color: blue;
  }

/* targets all paragraphs that are direct children of the .feature element */
  > p {
    font-size: 1.3em;
  }
}

Définir des relations explicites avec le sélecteur &

Vous pouvez également utiliser le sélecteur & pour être plus explicite lorsque vous imbriquez des règles de style. Considérez & comme un symbole représentant le sélecteur parent.

.feature {
 & button {
    color: blue;
  }
}

Cela équivaudrait à écrire les styles comme suit :

.feature button {
  color: blue;
}

Quand & est-il obligatoire ?

Sans &, les sélecteurs imbriqués seront des sélecteurs descendants du sélecteur parent. Pour former des sélecteurs composés, & est obligatoire.

.feature {
  &:last-child {
    /* Selects the .feature element that is the :last-child, equivalent to .feature:last-child */
  }
   
  & :last-child {
    /* Selects the :last-child inside of a .feature element, equivalent to .feature :last-child */
  }

  &.highlight {
    /* Selects .feature elements that also have a .highlight class, equivalent to .feature.highlight */
  }

  & .highlight {
     /* Selects elements inside of the .feature element with the class .highlight, equivalent to .feature .highlight */
  }
}

Vous pouvez également modifier le contexte et placer le sélecteur & à la fin du sélecteur enfant ou des deux côtés.


/* Targets buttons with an adjacent sibling button */
button {
  & + & {
    /* … */
  }
}
img {
  .my-component & {
    /* styles for images inside of `.my-component` ... */
  }
}

Dans le dernier exemple, nous ajoutons des styles pour les images à l'intérieur d'un élément avec la classe .my-component. Cela peut être utile si vous travaillez sur un projet dans lequel vous ne pouvez pas ajouter de class ni de id à un élément.

Nesting et spécificité

Comme :is(), le sélecteur d'imbrication reprend la spécificité du sélecteur le plus spécifique de la liste des sélecteurs parents.

#main-header,
.intro {
  & a {
    color: green;
  }
}

.intro a {
  color: blue;
}

La première règle cible tous les liens à l'intérieur des éléments #main-header et .intro, en leur attribuant une couleur verte.

La deuxième règle tente de remplacer cette règle pour rendre bleus les liens à l'intérieur de l'élément .intro.

Si nous examinons la spécificité de chaque règle, nous pouvons comprendre pourquoi cela ne fonctionne pas.

/* equivalent to :is(#main-header, .intro) a with a specificity of (1, 0, 1) */
#main-header,
.intro {
  & a {
    color: green;
  }
}

/* lower specificity of (0, 1, 1) */
.intro a {
  color: blue;
}

Étant donné que la première règle comporte un id dans sa liste de sélecteurs et que les règles imbriquées reprennent la spécificité du sélecteur le plus spécifique, elle présente une spécificité plus élevée que la deuxième règle. Les liens sont verts, même pour les éléments a qui ne se trouvent pas dans un élément avec le sélecteur #main-header.

Niveau d'imbrication non valide

Comme pour :is(), le sélecteur d'imbrication ne peut pas représenter de pseudo-éléments.

blockquote, blockquote::before, blockquote::after {
  color: navy;

  & {
    border: 1px solid navy;
  }
}

Vous vous attendriez à ce que blockquote et ses pseudo-éléments aient tous deux du texte et des bordures de couleur navy, mais ce n'est pas le cas. Étant donné que le sélecteur & ne peut pas représenter les pseudo-éléments, les styles de bordure imbriqués ne s'appliqueront qu'à la citation.

Lorsque vous créez des sélecteurs composés à l'aide de & et de sélecteurs de type, le sélecteur de type doit être placé en premier, sans espace entre les deux.

/* valid css nesting */
.feature {
  p& {
    font-weight: bold;
  }
}

/* invalid css nesting */
.feature {
  &p {
    font-weight: bold;
  }
}

Cette règle permet à l'imbrication CSS de fonctionner avec des outils de pré-traitement comme Sass. En Sass, l'écriture de &p ajouterait le sélecteur parent au sélecteur de type imbriqué, ce qui donnerait .featurep.

Imbriquer des règles @

Les règles de groupe conditionnel CSS telles que @container, @media, @supports et @layer peuvent également être imbriquées.

.feature {
  @media (min-width: 40em) {
    /* ... */
  }

  @container (inline-size > 900px) {
    /* ... */
  }
}

.feature {
  @supports (display: grid) {
    /* ... */
  }
}

.feature {
  @layer component {
    h2 {
      /* ... */
    }
  }
}

Vérifier que vous avez bien compris

Lorsque vous utilisez l'imbrication CSS, que représente le sélecteur & ?

Sélecteur enfant imbriqué
Pas tout à fait.
Sélecteur de parent
Bonne réponse !
sélecteur de frère le plus proche
Pas tout à fait.

Vous ne pouvez imbriquer que deux niveaux.

Vrai
Pas tout à fait.
Faux
Bonne réponse !

Quelles règles @ peuvent être imbriquées ?

@media
Bonne réponse !
@container
Bonne réponse !
@import
Pas tout à fait.
@supports
Bonne réponse !
@layer
Bonne réponse !