Heritiana Antonio Frond end Designer | Developer

Hello, I am

Heritiana Antonio

Frond end Designer | Developer

Chapitre 5 : @extend, et quand ne pas l’utiliser

1 min de lecture

Chapitre 5 : @extend, et quand ne pas l’utiliser

Objectif

À la fin de ce chapitre, vous savez regrouper des sélecteurs avec @extend, et vous savez quand ce regroupement embrouille le CSS plus qu’il ne l’allège. Vous ajoutez un encart commun à deux messages. Les boutons, eux, restent sur le mixin action : leurs couleurs ne sont pas les mêmes.

Le dossier mon-sass continue. La veille se lance depuis la racine :

npx sass --watch scss/style.scss css/style.css

_variables.scss, _carte.scss et _boutons.scss sont en place. index.html charge css/style.css. .bouton et .bouton-calme appellent boutons.action.

Ce que la copie répète

Dans css/style.css, display: inline-block est écrit pour .bouton, puis encore pour .bouton-calme. Le padding aussi. @include copie les déclarations à chaque appel : la source reste unique, le fichier livré répète. Ce doublon est juste, les fonds diffèrent. @extend sert quand deux sélecteurs portent exactement les mêmes déclarations et peuvent partager une seule règle.

Un placeholder

Ouvrez style.scss. Sous les boutons, nommez le bloc commun avec un sélecteur silencieux. Le nom commence par %. %encart n’est pas une classe : le navigateur ne peut pas le viser.

%encart {
  padding: variables.$espace-m;
  border: 1px solid variables.$encre;
  border-radius: 4px;
}

.succes {
  @extend %encart;
  border-color: #2a6f4e;
}

.erreur {
  @extend %encart;
  border-color: variables.$accent;
}

@extend est suivi du sélecteur à rejoindre. Les deux classes prennent le padding, la bordure et le rayon. Chacune ne réécrit que border-color. Le vert #2a6f4e reste écrit sur .succes : il n’entre pas dans _variables.scss. L’erreur reprend le bordeaux déjà nommé, variables.$accent, soit #9b2226.

Ajoutez les deux phrases dans index.html, sous les liens.

<p class="succes">Le fichier est enregistré.</p>
<p class="erreur">Le champ nom est vide.</p>

Enregistrez. La veille réécrit css/style.css. Le placeholder a disparu. Les deux classes occupent une seule règle, et Sass place .erreur devant .succes :

.erreur, .succes {
  padding: 1rem;
  border: 1px solid #1f2933;
  border-radius: 4px;
}

.succes {
  border-color: #2a6f4e;
}

.erreur {
  border-color: #9b2226;
}

variables.$espace-m vaut 1rem, variables.$encre vaut #1f2933. Il ne reste ni @extend, ni signe %. La règle commune sort là où %encart était écrit. Les deux border-color viennent ensuite et recouvrent le #1f2933. Sass a écrit .erreur avant .succes dans la virgule : pour le navigateur, l’ordre de cette liste ne change pas les éléments visés.

Si personne n’étend %encart, Sass n’écrit pas la règle. Un placeholder sans appel ne laisse rien dans le CSS. Une classe .encart, elle, serait livrée même si le HTML ne l’utilise pas.

Étendre le bouton

On est tenté d’économiser le second appel. Remplacer le corps de .bouton-calme par une seule ligne :

.bouton-calme {
  @extend .bouton;
}

Le compilateur accepte. Le second lien n’est plus clair. Il a rejoint chaque règle qui contenait .bouton :

.bouton, .bouton-calme {
  display: inline-block;
  padding: 0.5rem 1rem;
  background: #1f2933;
  color: #f7f3ea;
  text-decoration: none;
}

.bouton:hover, .bouton-calme:hover {
  background: #9b2226;
  color: #f7f3ea;
}

.bouton.est-inactif, .est-inactif.bouton-calme {
  opacity: 0.5;
}

Le fond encre et le texte papier viennent avec le sélecteur, ainsi que le survol. L’état inactif aussi, sous une forme réécrite : .est-inactif.bouton-calme. Un élément qui porte les deux classes est encore visé. Toute règle future écrite sur .bouton s’appliquera aussi à .bouton-calme.

@extend ne prend pas d’argument. Il ne peut pas recevoir $fond ni $encre-bouton. Ajouter ensuite un @include pour repeindre le fond remet les couleurs claires, et copie une seconde fois display, le padding et le survol. L’écran peut sembler juste. Le CSS, lui, raconte deux histoires pour une classe. Remettez l’appel du chapitre 4 :

.bouton-calme {
  @include boutons.action(
    $fond: variables.$papier,
    $encre-bouton: variables.$encre
  );
  border: 2px solid variables.$encre;
}

Le mixin reste le bon outil dès qu’une valeur change, ou dès que le bloc contient &.

Le sélecteur qui s’allonge

@extend ne réécrit pas seulement le bloc où le sélecteur est défini. Il réécrit aussi chaque sélecteur qui le contient. Avec une imbrication, la liste grandit. Collez cet essai plus bas dans style.scss, sans remplacer l’encart.

.message {
  border: 1px solid variables.$encre;
  padding: variables.$espace-m;

  .sidebar & {
    font-size: 0.9rem;
  }

  .footer & {
    font-size: 0.8rem;
  }
}

.note {
  @extend .message;
}

Le CSS livré contient trois regroupements :

.message, .note {
  border: 1px solid #1f2933;
  padding: 1rem;
}

.sidebar .message, .sidebar .note {
  font-size: 0.9rem;
}

.footer .message, .footer .note {
  font-size: 0.8rem;
}

Deux contextes, deux règles de plus. Dix contextes, dix règles, et .note les reçoit toutes, y compris celles prévues seulement pour .message. Ce gonflement est le comportement normal, pas un message d’erreur. Il reste lisible sur un placeholder court, sans & et sans parent. Il ne l’est plus sur une classe déjà imbriquée. Effacez l’essai .message : .erreur ne doit étendre que %encart.

Deux refus du compilateur

Un @extend écrit dans une media query ne rejoint pas un sélecteur écrit en dehors.

.encart {
  padding: variables.$espace-m;
}

@media (min-width: 40rem) {
  .large {
    @extend .encart;
  }
}

Dart Sass s’arrête et affiche You may not @extend selectors across media queries. Le message marque .encart comme sélecteur étendu, puis la media query. Effacez cet essai.

L’autre sens ne s’arrête pas, et c’est un autre piège. Si .encart est à l’intérieur de la media et que .large, écrit dehors, l’étend, le compilateur tire .large dans la media. Le CSS ne garde pas .large à la racine. En dessous de 40rem, ce padding n’existe pas. Deux sélecteurs écrits dans la même media, en revanche, se regroupent, et la règle reste dans ce bloc.

Le compilateur refuse aussi les sélecteurs composés et les sélecteurs complexes. @extend .bouton:hover et @extend .sidebar .message s’arrêtent. Le premier signal indique que les compound selectors ne s’étendent plus. Le second dit complex selectors may not be extended. On étend un seul sélecteur simple, %encart ou .message, pas une combinaison.

Si le nom n’existe pas, la compilation échoue : The target selector was not found. Le message propose !optional. Ce drapeau fait taire l’erreur et n’ajoute rien : la classe reste seule, sans le padding attendu. On corrige le nom.

Exercice

Reprenez mon-sass. index.html reste lié à css/style.css.

  1. Dans style.scss, sous les boutons, ajoutez %encart, .succes et .erreur. Ne touchez pas à _boutons.scss.
  2. Ajoutez les deux paragraphes dans index.html. Rechargez : même boîte, bordure verte d’un côté, bordeaux de l’autre.
  3. Dans css/style.css, %encart est absent. padding: 1rem est partagé dans .erreur, .succes. display: inline-block reste écrit deux fois, une fois par bouton.
  4. Remplacez le corps de .bouton-calme par @extend .bouton;. Rechargez : le second lien devient foncé, et .est-inactif.bouton-calme apparaît. Remettez l’@include avec les arguments nommés et la bordure.
  5. Collez l’essai .message, avec .sidebar & et .footer &, et .note qui étend .message. Lisez les trois règles groupées, puis effacez l’essai.
  6. Écrivez @extend .encart dans une media query alors que .encart est dehors. Lisez le message, puis effacez l’essai.

C’est réussi quand les deux messages partagent une règle, quand les deux boutons passent encore par action, et quand les deux essais ont disparu de style.scss. Si le second lien reste clair à l’étape 4, l’@include est encore dans la règle : le @extend s’y est ajouté au lieu de le remplacer.

À retenir

  • @extend ajoute un sélecteur à des règles déjà écrites. Il ne prend pas d’argument.
  • %encart ne sort dans le CSS que s’il est étendu. Une classe ordinaire sort même sans être utilisée.
  • Étendre .bouton attache .bouton-calme au fond, au survol et à .est-inactif.
  • L’imbrication multiplie les sélecteurs. Un @extend dans une media query ne rejoint pas un sélecteur écrit dehors.
  • On étend un sélecteur simple. !optional cache un nom absent au lieu de le réparer.
  • Une valeur qui change appelle un mixin. Un bloc vraiment identique, sans imbrication, peut passer par un placeholder.

La suite

Le placeholder partage un bloc figé. Il ne calcule rien : pas de somme, pas de choix parmi des couleurs, pas de répétition pour cinq variantes. La leçon suivante, « Fonctions, maps et boucles », écrit ces calculs dans la source. Vous garderez mon-sass, le mixin action, les deux boutons et l’encart %encart.