Cours CSS / organiser les fichiers, l’imbrication et les couches de cascade

Un CSS maintenable sépare les valeurs partagées, les styles de base, les composants et les ajustements conditionnels selon leurs rôles. L'imbrication native définit la portée des sélecteurs liés, tandis que les couches de cascade ordonnent explicitement les groupes de règles et introduisent leurs propres règles de priorité.

Réduire la duplication et attribuer les responsabilités des fichiers

Tout d'abord, recherchez les doublons de la même couleur et espacement, les sélecteurs obsolètes et les règles de bouton presque identiques. Les valeurs ayant le même rôle sont regroupées dans des variables, et différents boutons divisent les règles communes et les différences nécessaires. Il peut être préférable de conserver deux composants indépendants plutôt que de créer des conditions complexes pour éliminer toute duplication. Lors du regroupement des valeurs communes, vérifiez la portée et les conditions d'héritage de la variable dans Variables CSS : var(), portée, héritage et valeurs de repli.

/* Before cleanup: Same value repeated in multiple places */
.lesson-button { padding: .65rem 1rem; color: #166b92; }
.notice-button { padding: .65rem 1rem; color: #166b92; }

/* After organizing: sharing common shapes and roles */
.button { padding: .65rem 1rem; color: var(--accent); }
.button.text-alert { color: #9d2632; }

La comparaison ci-dessus est un code partiel expliquant la direction du nettoyage. Une classe de bouton commune doit également être attachée au HTML réel, et si vous ne faites que renommer le CSS et laissez le HTML tel quel, l'apparence peut disparaître. L'exercice complet ci-dessous exécute cette configuration décomposée en fichiers et en couches.

fichier responsabilité Jugement lors de la révision
tokens.css Couleurs et espacements communs Est-ce une valeur partagée par plusieurs composants ?
base.css Police, marge par défaut, affichage du focus Est-ce le comportement par défaut pour l'ensemble du document ?
layout.css Zone de contenu et mise en page S'agit-il de la relation de position des composants ?
components.css Forme et état des cartes et des boutons Est-ce lié à un seul composant ?
utilities.css Petites classes auxiliaires explicites S'agit-il d'une exception qui ne s'appliquera que dans quelques endroits ?

Pour un petit document, des sections séparées par des commentaires dans un seul fichier peuvent suffire. Envisagez de séparer les fichiers lorsque plusieurs personnes modifient différents composants ou lorsque le champ de réutilisation augmente. Le nom du fichier est un séparateur pour la navigation, et le simple fait de diviser le fichier n’élimine pas les conflits de sélecteurs. Les chemins de connexion HTML et les cascades doivent également être gérés.

Distinguez le parent et les descendants dans l’imbrication

L’imbrication CSS native est interprétée directement par le navigateur. Cela diffère de la manière dont les préprocesseurs comme Sass génèrent un CSS différent durant la phase de construction. Le code ci-dessous place le titre de la .card proche de l’état de la carte elle-même.

.card {
  & h2 { color: var(--accent); }
  &:hover { border-color: var(--accent); }
}

& h2 représente le h2 à l'intérieur de la carte, et &:hover représente le survol de la carte elle-même. Si vous insérez un espace comme & :hover, le résultat sera différent car il sélectionne le descendant survolé. Distinguez également &.warning, qui sélectionne une classe auxiliaire du même élément, et & .warning, qui sélectionne des descendants.

/* The simple overlap above is expanded */
.card h2 { color: var(--accent); }
.card:hover { border-color: var(--accent); }

Avec ce sélecteur parent unique, l'imbrication peut être comparée directement avec les règles développées. Une liste de parents séparés par des virgules peut faire en sorte que la spécificité du sélecteur imbriqué dépende du sélecteur le plus spécifique de cette liste. L'imbrication complexe n'est donc pas une simple concaténation de chaînes. Évitez une profondeur inutile qui lie le CSS à une hiérarchie HTML rigide. Pour plus de détails, voir le guide d'utilisation de l'imbrication MDN, et l'explication de la priorité .

Vérifiez la prise en charge de l'imbrication par le navigateur que vous devez supporter et, si vous avez besoin d'un environnement qui ne la prend pas en charge, choisissez le CSS développé comme ci-dessus ou un outil de construction approprié. Ce n'est pas parce que vous utilisez une nouvelle syntaxe qu'elle s'applique à tous vos visiteurs.

Comparez les couches de cascade avant la spécificité

Pour les déclarations normales des feuilles de style de l’auteur, les règles hors couche l’emportent sur les règles placées dans une couche. Entre couches, la couche déclarée plus tard dans l’ordre établi est prioritaire : sa classe peut donc l’emporter sur un sélecteur d’ID d’une couche antérieure. À couche et importance égales, on compare ensuite la spécificité, la proximité de portée lorsqu’elle intervient, puis l’ordre des déclarations. Retrouvez les bases dans les règles de spécificité et de cascade.

@layer tokens, base, layout, components, utilities;
@layer base { #layer-target { color: #183044; } }
@layer utilities { .text-alert { color: #9d2632; } }

Le bouton exemple correspond aux deux sélecteurs. Dans la déclaration générale, la couche utilities est déclarée après base, donc la couleur rouge s'applique. Prédire le résultat simplement parce qu'un bouton a une priorité d'ID élevée peut être incorrect. L'ordre des couches est déterminé par l'ordre dans lequel leurs noms sont enregistrés pour la première fois, et même si vous rouvrez la même couche plus tard, cette couche ne sera pas déplacée à l'arrière.

Dans cette comparaison de feuilles de style de l’auteur, les déclarations !important sont prioritaires sur les déclarations normales, mais l’ordre des couches est alors inversé. Les déclarations importantes des couches déclarées plus tôt l’emportent sur celles des couches suivantes ; les déclarations importantes dans une couche l’emportent aussi sur celles qui sont hors couche. Cette démonstration ne remplace pas l’ensemble de la cascade : l’origine utilisateur ou navigateur, les styles en ligne et les transitions en cours demandent également leurs propres comparaisons. Consultez la documentation MDN sur @layer.

Créez le fichier HTML et cinq fichiers CSS

Créez la structure ci-dessous dans un dossier vide. Tous les fichiers sont enregistrés en UTF-8. Comme index.html utilise le chemin relatif styles/..., le fichier CSS doit se trouver dans le sous-dossier styles. Vous pouvez ouvrir index.html avec un navigateur sans serveur séparé ni bibliothèque externe. Si vous ne pouvez pas lire le fichier, comparez l'emplacement du fichier et l'adresse de connexion avec Appliquer CSS avec des styles en ligne, internes et externes.

stylesheet-project/
  index.html
  styles/
    tokens.css
    base.css
    layout.css
    components.css
    utilities.css

index.html contient les balises link et les classes réelles. Le bouton de comparaison n'est pas un bouton pour exécuter une action, mais il est destiné à des fins d'entraînement pour vérifier la couleur du texte appliquée et le focus.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>CSS files and layers by responsibility</title>
  <link rel="stylesheet" href="styles/tokens.css">
  <link rel="stylesheet" href="styles/base.css">
  <link rel="stylesheet" href="styles/layout.css">
  <link rel="stylesheet" href="styles/components.css">
  <link rel="stylesheet" href="styles/utilities.css">
</head>
<body>
  <main class="demo">
    <h1>Organizing a small stylesheet</h1>
    <article class="card">
      <h2>Scope nested rules to the card</h2>
      <p>The card heading uses the accent color. Compare it with the heading outside the card.</p>
      <button class="button" type="button">Regular button</button>
    </article>
    <section><h2>Heading outside the card</h2><p>Use the default font color.</p></section>
    <button id="layer-target" class="button text-alert" type="button">Layer comparison button</button>
  </main>
</body>
</html>

styles/tokens.css enregistre d'abord l'ordre des couches et déclare les valeurs communes. Un autre fichier ouvre une couche portant le même nom pour y ajouter des règles.

@layer tokens, base, layout, components, utilities;
@layer tokens {
  :root { --ink: #183044; --accent: #166b92; --space-card: 1.25rem; }
}

styles/base.css définit l'apparence par défaut du document et l'indicateur de focus. Les règles ID sont des dispositifs mis en place pour comparer l'impact des couches sur la priorité.

@layer base {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; padding: 1rem; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
  h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
  button { font: inherit; cursor: pointer; }
  :focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
  #layer-target { color: #183044; }
}

styles/layout.css gère l'espacement entre les zones de contenu et les sections.

@layer layout {
  .demo { display: grid; gap: 1rem; max-width: 44rem; margin-inline: auto; }
  .demo > h1 { margin-block: 0 .5rem; }
  .demo > button { justify-self: start; }
}

styles/components.css gère les cartes et les boutons. La couleur du titre ne s'applique qu'à l'intérieur de la carte, et la bordure change lorsque vous survolez la carte ou que le bouton intérieur reçoit le focus.

@layer components {
  .card {
    padding: var(--space-card); border: 2px solid #90a4b2; border-radius: .5rem;
    & h2 { margin-top: 0; color: var(--accent); }
    &:hover, &:focus-within { border-color: var(--accent); }
  }
  .button {
    padding: .65rem 1rem; color: var(--accent); background: #eaf4fa;
    border: 1px solid #166b92; border-radius: .3rem;
  }
}

styles/utilities.css gère une classe auxiliaire pour le bouton de comparaison. Les déclarations générales dans ce fichier ont la priorité sur les règles ID/bouton des couches précédentes.

@layer utilities {
  .text-alert { color: #9d2632; }
}

Tester séparément les fichiers, l’imbrication et les couches

  1. Sur l'écran principal, assurez-vous que le titre de la carte et le bouton général sont bleus, que le titre à l'extérieur de la carte est de couleur par défaut, et que le bouton de comparaison est rouge. Utilisez Tab pour vérifier si le bouton possède un indicateur de focus.
  2. Commentez uniquement la règle & h2 à l'intérieur de la carte dans components.css. Les titres des cartes reviennent également à leurs couleurs par défaut. Après confirmation, décommentez-la.
  3. Attachons la classe text-alert aux deux boutons dans le HTML. Les boutons à l'intérieur de la carte seront également rouges. Restaurer uniquement les classes supplémentaires du bouton interne en les supprimant.
  4. À la fin de components.css, ajoutez uniquement .button { color: #28633b; } en dehors du bloc @layer. Les deux boutons deviendront verts. La déclaration normale hors couche l’emporte sur les déclarations normales placées dans les couches. Après confirmation, supprimez la nouvelle règle.
  5. Ajoutez !important uniquement à la déclaration de couleur de l'ID dans base.css. Le bouton de comparaison revient à la couleur par défaut. Dans cet état, si vous ajoutez !important à la couleur de utilities.css, la couleur de base de la couche précédente sera maintenue. Après comparaison, supprimez les deux ajouts de !important.
  6. Supprimez simplement temporairement le lien components.css du HTML. Les formes des composants de la carte et du bouton disparaissent. Reconnectez le chemin à son état original et actualisez pour le restaurer.

Diagnostiquer les styles manquants

Vérifiez séparément les problèmes de séparation de fichiers et les conflits de sélecteurs. Commencez par regarder dans Réseau ou Sources dans les outils de développement de votre navigateur pour voir si le fichier a réellement été chargé, puis sélectionnez l'élément pour voir si les règles correspondent dans Styles et si des déclarations ont été écrasées. Si vous ne voyez pas le fichier, vérifiez le chemin relatif, le nom du fichier et l'extension, et si vous ne voyez pas la règle, vérifiez la classe et la prise en charge de la syntaxe.

Même s'il existe une règle correspondante, si un texte barré apparaît, la couche, l'important, le sélecteur, la portée et l'ordre sont comparés. Si les variables communes ne sont pas comme prévu, il faut également examiner la portée de déclaration des variables. Après la séparation, il est utile pour la maintenance de revérifier l'écran principal et le focus/survol, et de corriger les divisions de responsabilités et les sélecteurs qui ont causé des conflits au lieu de continuer à ajouter des déclarations important inutiles.