Kodokon kodokon.com

Architecture CSS : BEM, utility-first, organisation des fichiers

Comparez BEM et l'approche utility-first, leurs compromis réels en équipe, et structurez vos fichiers avec les cascade layers.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

Le CSS ne casse pas à l'échelle d'un composant, il casse à l'échelle d'un projet : spécificité qui grimpe pour « gagner » contre une règle existante, peur de supprimer du code dont on ignore la portée, styles qui fuient d'un écran à l'autre. Une architecture CSS est avant tout une stratégie de gestion de la spécificité et de la portée. Les deux écoles dominantes, BEM et utility-first, répondent au même problème par des voies opposées.

BEM (Block, Element, Modifier) nomme chaque nœud stylé : .card (bloc autonome), .card__title (élément appartenant au bloc), .card--featured (variante du bloc). La convention garantit une spécificité plate - une seule classe partout, jamais de sélecteur descendant - donc aucun conflit d'ordre, et un nom qui documente l'appartenance. Le coût : inventer des noms en permanence, et une duplication réelle des valeurs (le même margin-top: 16px réécrit dans vingt blocs).

CSS
.card {
  padding: 16px;
  border-radius: 8px;
}

.card__title {
  font-size: 1.25rem;
}

.card__title--muted {
  color: #6b7280;
}

.card--featured {
  border: 2px solid var(--brand);
}
BEM : spécificité plate, appartenance explicite

L'approche utility-first (Tailwind en est l'incarnation dominante) renverse la logique : plus de noms, des classes atomiques à responsabilité unique composées dans le HTML. Les bénéfices sont mesurables : zéro effort de nommage, styles colocalisés avec le markup (supprimer le composant supprime ses styles), et un CSS final quasi constant en taille puisque les utilitaires sont partagés. Les compromis aussi : un HTML verbeux, une duplication déplacée vers le markup qui exige une couche de composants (React, Vue, partials) pour rester DRY, et une courbe d'apprentissage du vocabulaire.

HTML
<article class="flex flex-col gap-4 rounded-lg
  border border-gray-200 p-4 shadow-sm">
  <h2 class="text-lg font-semibold">Facture</h2>
  <p class="text-sm text-gray-600">
    Payée le 12 mars
  </p>
</article>
Utility-first : les styles vivent dans le markup

Ces approches ne s'excluent pas : beaucoup d'équipes matures composent - utilitaires pour le layout et l'espacement, classes de composants (BEM ou CSS Modules) pour les motifs complexes et les états. Côté organisation des fichiers, le principe ITCSS reste la référence : ordonner du générique au spécifique (reset, éléments de base, composants, utilitaires). La règle native @layer transforme cette convention en garantie du moteur : la priorité entre couches est indépendante de la spécificité des sélecteurs qu'elles contiennent.

CSS
@layer reset, base, components, utilities;

@layer base {
  h2 { font-size: 1.5rem; }
}

@layer components {
  #hero .btn { padding: 12px 24px; }
}

@layer utilities {
  .p-0 { padding: 0; }
}
Cascade layers : l'ordre prime sur la spécificité

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Dans .card__title--muted, que désigne le segment --muted ?
    • Une custom property CSS
    • Un modificateur : une variante de l'élément card__title
    • Un élément enfant de card__title
    • Un état géré par JavaScript
  2. Quel est l'apport principal de @layer dans la cascade ?
    • Il augmente la spécificité des sélecteurs de la couche
    • Il fait primer l'ordre des couches sur la spécificité des sélecteurs
    • Il isole les styles d'un composant comme le ferait un Shadow DOM
    • Il charge les couches en parallèle pour accélérer le rendu
  3. Quel compromis assume-t-on en adoptant une approche utility-first ?
    • Un fichier CSS qui grossit proportionnellement au nombre de pages
    • Une spécificité élevée difficile à surcharger
    • Un HTML verbeux dont la déduplication repose sur une couche de composants