La bible de la nomenclature CSS

Non pas « comment nommer », mais quand nommer, quand refuser de nommer, et comment vivre avec ses noms dans deux ans.

💡 Le principe qui gouverne tout le reste : une nomenclature ne sert pas à décrire ce qu'un élément ressemble, mais ce qu'il est et à quoi il sert. Un nom qui décrit l'apparence devient un mensonge dès la première refonte.

Les quatre familles de classes

c- Composant — un objet autonome et réutilisable : bouton, carte, badge, modale.
l- Layout — une structure qui positionne les composants : section, grille, conteneur.
u- Utilitaire — une classe à responsabilité unique et composable : alignement, taille de texte.
is- / has- État — une condition temporaire et dynamique : ouvert, actif, en erreur.

Comment lire ce document

L'ordre du menu est un ordre d'apprentissage, pas un simple classement. Si tu débutes, lis dans l'ordre. Les trois pages qui font vraiment la différence entre un débutant et un pro sont, dans l'ordre :

  1. Décider de nommer — parce que le plus dur n'est pas de nommer, c'est de résister à l'envie de nommer.
  2. Anti-patterns — parce qu'on apprend plus vite de ce qui casse que de ce qui marche.
  3. Cascade & spécificité — parce que c'est la source n°1 de frustration, et que sans ça on finit en guerre de !important.

Le squelette d'une page bien nommée

l-section                    ← le layout pose la structure
  └─ l-container             ← centre et limite la largeur
      └─ l-grid              ← répartit les enfants
          ├─ c-card          ← un composant autonome
          │   ├─ c-card__img       ← son élément enfant (BEM)
          │   └─ c-card__title
          ├─ c-card c-card--featured   ← une variation (BEM)
          └─ c-card is-loading         ← un état temporaire
🔑 À retenir dès maintenant : le layout dit , le composant dit quoi, l'utilitaire dit un détail précis, l'état dit dans quelle condition. Si tu ne sais pas dans quelle famille ranger une classe, c'est presque toujours qu'elle n'a pas lieu d'exister.

Décider de nommer

La compétence la plus rare, et celle qu'aucune documentation n'enseigne : savoir ne pas créer une classe.

⛔ Le piège du débutant : après avoir lu un guide de nomenclature, on veut tout nommer. On se retrouve avec 200 classes globales, dont 140 ne sont utilisées qu'une seule fois. Ce n'est pas un design system, c'est un cimetière.

La règle de trois

Empruntée au refactoring logiciel, elle s'applique parfaitement au CSS :

1
Ça apparaît une fois sur le site

Ne nomme rien. Style local sur l'élément, dans Elementor. Une classe globale utilisée une fois est un coût de maintenance sans contrepartie.

2
Ça apparaît deux fois à l'identique

Attends encore. Deux occurrences peuvent être une coïncidence — deux choses qui se ressemblent aujourd'hui mais évolueront différemment demain. Duplique le style, note-le mentalement.

3
Ça apparaît trois fois

Maintenant tu nommes. Trois occurrences, ce n'est plus une coïncidence : c'est un motif. Crée la classe globale, remplace les trois instances.

L'arbre de décision complet

Avant de créer une classe, pose-toi ces questions dans cet ordre :

1. Elementor le fait-il déjà nativement ?

Espacement entre enfants → réglage Gap du Container. Hiérarchie de titres → Typographie globale. Couleur de marque → Variable. Si oui : ne crée pas de classe, utilise l'outil natif. Une classe qui double une fonctionnalité native est une dette pure.

2. Est-ce un objet, ou un attribut ?

Un objet (une carte, un bouton) → composant c-. Un attribut isolé (centrer, masquer) → utilitaire u-. Une structure qui contient d'autres choses → layout l-. Si tu n'arrives pas à trancher, c'est souvent que ton élément fait trop de choses : découpe-le.

3. Est-ce permanent, ou temporaire ?

Permanent (c'est une sorte de bouton) → variation BEM --primary. Temporaire (il est actuellement désactivé) → état is-disabled. Un état ne se met jamais dans le nom du composant.

4. Le nom survivrait-il à une refonte graphique ?

Fais le test mentalement : « si demain le client passe du bleu au vert, ce nom ment-il ? ». Si oui, tu as nommé l'apparence. Recommence en nommant le rôle.

Le test du nouveau venu

Le meilleur juge de la qualité d'un nom : un développeur qui découvre le projet.

Peut-il, en lisant uniquement la liste des classes, sans ouvrir une seule page, deviner à quoi ressemble le site ? Si oui, ta nomenclature est bonne. Si les noms sont .box-2, .custom-thing, .section-new, il ne peut rien deviner — et toi non plus, dans six mois.

🔑 La bonne question n'est jamais « quel nom donner ? » mais « ai-je besoin d'un nom ? ». Un design system se juge à sa taille : le meilleur est le plus petit qui couvre le besoin. Chaque classe que tu ajoutes est une chose de plus à maintenir, documenter, et expliquer.

Conventions d'écriture

Les règles ennuyeuses. Ce sont exactement celles qui font diverger un projet quand on ne les fixe pas au départ.

1. La langue : anglais, toujours

✗ À éviter
.c-bouton-principal
.l-entete

Mélange deux langues avec le CSS lui-même (qui est en anglais), et devient illisible pour tout développeur tiers.

✓ À faire
.c-btn-primary
.l-header

Cohérent avec le CSS, les frameworks, la documentation Elementor, et n'importe quel dev que tu embaucheras.

Ton interface peut être en français. Tes noms de classes, non. C'est du code.

2. La casse : kebab-case, uniquement

FormeExempleVerdict
kebab-case.c-btn-primary✓ La norme CSS. Utilise ça.
camelCase.cBtnPrimary✗ Convention JavaScript, pas CSS.
PascalCase.CBtnPrimary✗ Convention de composant React.
snake_case.c_btn_primary✗ Entre en conflit visuel avec le __ de BEM.

3. Les séparateurs BEM

.c-card              ← le bloc
.c-card__title       ← un élément (double underscore)
.c-card--featured    ← une variation (double tiret)
.c-card__title--big  ← une variation d'élément

/* JAMAIS de chaînage d'éléments */
.c-card__body__title   ✗  ← le DOM peut changer, ce nom casse
.c-card__title         ✓  ← plat, robuste
⚠️ Règle souvent violée : BEM est plat, pas hiérarchique. Même si ton titre est imbriqué à 3 niveaux dans le DOM, il s'appelle .c-card__title, pas .c-card__header__row__title. Le nom décrit l'appartenance au bloc, pas le chemin dans le DOM.

4. Les abréviations autorisées

Une abréviation n'est acceptable que si elle est universellement comprise. Dans le doute, écris le mot en entier.

✓ Autorisées
btn · nav · img · img
col · msg · alt

Tout le monde les lit sans réfléchir.

✗ Proscrites
ctn · wrp · hdr
bg2 · txt-sm-b

Tu économises 4 caractères et tu perds 30 secondes à chaque relecture.

5. Singulier ou pluriel ?

Règle simple : l'objet est au singulier, la collection au pluriel.

.c-card       ← un objet carte
.l-cards      ← la grille qui contient plusieurs cartes
.c-nav-item   ← un item de navigation
.c-nav        ← la navigation entière

6. Ordre des mots : du général au spécifique

✗ Désordonné
.c-primary-btn
.c-large-card-featured

Impossible à trier alphabétiquement de façon utile. Les boutons ne sont pas regroupés.

✓ Du général au spécifique
.c-btn--primary
.c-card--featured

Tri alphabétique = regroupement logique. Tous les c-btn se suivent dans le Class Manager.

🔑 Pourquoi c'est si important : ces conventions n'ont aucun impact sur le rendu. Elles ont un impact énorme sur ta vitesse de travail dans six mois, et sur la capacité d'un collaborateur à reprendre ton projet sans t'appeler.

c- Composant

Un objet autonome, auto-contenu, qui fonctionne indépendamment de son environnement.

Le test du composant

Une classe mérite le préfixe c- si elle passe ce test : « puis-je découper cet élément, le coller ailleurs sur le site, et attendre qu'il fonctionne et reste beau ? »

Si oui → composant. Si non (il dépend de son parent pour sa largeur, son alignement, sa position) → ce n'est pas un composant, c'est du layout ou un élément enfant.

Exemples canoniques

  • .c-btn — un bouton
  • .c-card — une carte de contenu
  • .c-badge — une étiquette
  • .c-alert — un message d'alerte
  • .c-modal — une fenêtre modale
  • .c-nav — une navigation
  • .c-input — un champ de formulaire
  • .c-avatar — une photo de profil

Anatomie BEM d'un composant

<article class="c-card c-card--featured">
  <img class="c-card__img" src="...">
  <h3 class="c-card__title">Titre</h3>
  <p class="c-card__text">Description</p>
  <a class="c-btn c-btn--secondary">Lire plus</a>
</article>
PartieSyntaxeCe que ça veut dire
Bloc.c-cardL'objet lui-même. Il vit seul.
Élément.c-card__titleUne partie du bloc. Elle n'a pas de sens hors du bloc.
Variation.c-card--featuredUne sorte de carte. Permanente, pas dynamique.

Le composant ne se positionne pas lui-même

⛔ Règle d'or : un composant ne définit jamais sa propre marge externe, sa largeur, ou sa position. Ce sont des décisions qui appartiennent au contexte, donc au layout parent. Un .c-card qui contient margin-bottom: 2rem ne sera plus réutilisable dans une grille qui gère déjà son gap.
✗ Composant qui se positionne
.c-card {
  margin-bottom: 2rem;
  width: 300px;
  float: left;
}

Impossible à réutiliser ailleurs sans se battre contre ces règles.

✓ Composant autonome
.c-card {
  padding: 1.5rem;
  border-radius: 8px;
  background: var(--color-surface);
}
/* le parent gère le reste */
.l-grid { gap: 2rem; }

Réutilisable partout. Le parent décide de l'espacement.

🔑 Formule à retenir : un composant gère son intérieur (padding, couleur, bordure, typo). Son extérieur (marge, largeur, position) appartient au parent.

l- Layout

La structure invisible qui organise l'espace. Elle ne se voit pas — elle se ressent.

Ce qu'un layout gère (et rien d'autre)

✓ Son domaine

Display (flex, grid) · Gap · Padding · Largeur max · Alignement des enfants · Nombre de colonnes · Comportement responsive

✗ Pas son domaine

Couleur de fond décorative · Typographie · Bordures · Ombres · Tout ce qui relève de l'apparence plutôt que de la structure

Exemples canoniques

  • .l-section — une bande horizontale pleine largeur
  • .l-container — centre et limite la largeur du contenu
  • .l-grid — répartit des enfants en colonnes
  • .l-stack — empile des enfants verticalement avec un gap
  • .l-cluster — aligne des enfants horizontalement, avec retour à la ligne
  • .l-sidebar — contenu principal + colonne latérale

Les trois layouts qui couvrent 90% des besoins

/* 1. STACK — empiler verticalement (le plus utilisé) */
.l-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* 2. CLUSTER — aligner horizontalement, avec retour à la ligne */
.l-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

/* 3. GRID — répartir en colonnes égales */
.l-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
🔑 Le auto-fit + minmax : cette seule ligne rend ta grille responsive sans une seule media query. Les colonnes se réorganisent toutes seules selon la place disponible. C'est le genre de règle qui remplace 40 lignes de breakpoints.

Le layout dans Elementor v4

Le Flexbox Container d'Elementor est ta classe layout. Il expose nativement direction, gap, alignement, padding — exactement le domaine du l-.

⚠️ Conséquence pratique : tu n'as souvent pas besoin de créer de classes l- personnalisées dans Elementor. Configure le Container nativement et lie ses valeurs à tes variables. Ne crée une classe l- que si la même configuration structurelle se répète 3+ fois (cf. règle de trois).

u- Utilitaire

Une classe, une propriété, une responsabilité. Puissant — et dangereux si mal dosé.

La définition stricte

Un utilitaire fait une seule chose, ne dépend de rien, et fonctionne partout. S'il fait deux choses, ce n'est plus un utilitaire, c'est un mini-composant mal nommé.

/* ✓ Utilitaires — une propriété chacun */
.u-text-center { text-align: center; }
.u-hidden      { display: none; }
.u-mb-4        { margin-bottom: var(--space-4); }

/* ✗ Ce n'est plus un utilitaire */
.u-fancy-box {
  padding: 1rem;
  border: 1px solid;
  border-radius: 8px;
  background: white;
}   ← ça, c'est un composant. Appelle-le .c-box.

Le grand danger : le style inline déguisé

⛔ L'anti-pattern majeur : quand tu empiles 6 utilitaires sur chaque élément, tu as réinventé le style inline, en plus verbeux. Tu n'as gagné aucune réutilisabilité — tu as juste déplacé le problème.
✗ Soupe d'utilitaires
<div class="u-p-4 u-mb-6 u-bg-white
  u-rounded u-shadow u-border
  u-flex u-flex-col u-gap-3">

Ce motif se répète sur 12 pages. Le jour où le padding change, tu édites 12 pages.

✓ Le motif est nommé
<div class="c-card">

/* et une seule fois : */
.c-card {
  padding: var(--space-4);
  /* ...etc */
}

Un seul point de modification. C'est ça, un design system.

Le seuil de bascule

🔑 Règle chiffrée : au-delà de 3 utilitaires empilés sur un même élément, et si ce groupe se répète, arrête-toi. Tu ne décores plus — tu es en train de décrire un composant sans lui donner de nom. Nomme-le.

Quand l'utilitaire est le bon choix

Dans tous les autres cas, demande-toi si le composant ne devrait pas porter ce style lui-même.

is- / has- États

La famille oubliée — et pourtant indispensable dès qu'il y a un menu, un accordéon ou un formulaire.

Variation ≠ état

C'est la distinction la plus mal comprise de tout BEM. Elle est pourtant simple :

Variation (--)État (is- / has-)
NaturePermanente, décidée au designTemporaire, change à l'exécution
Qui la poseToi, en construisant la pageLe JavaScript, en réponse à une action
Question« C'est quelle sorte de bouton ? »« Dans quelle condition est-il, là, maintenant ? »
Exemple.c-btn--primary.is-loading

La convention

/* is- : l'élément EST dans cet état */
.is-active     ← onglet sélectionné, lien de nav courant
.is-open       ← menu déplié, accordéon ouvert
.is-hidden     ← masqué dynamiquement
.is-loading    ← en attente d'une réponse
.is-disabled   ← non cliquable

/* has- : l'élément CONTIENT quelque chose */
.has-error     ← un champ dont la validation a échoué
.has-children  ← un item de menu avec un sous-menu
.has-icon      ← un bouton qui contient une icône

L'état est un modificateur global, pas un préfixe de bloc

✗ État dans le nom du bloc
.c-menu--open
.c-btn-disabled

Tu devras dupliquer --open pour chaque composant. Et le JS devra connaître le nom du composant pour poser la classe.

✓ État autonome
.c-menu.is-open
.c-btn.is-disabled

Une seule classe .is-open pour tout le site. Le JS fait el.classList.toggle('is-open'), quel que soit le composant.

En CSS

/* On scope l'état au composant, pour éviter les collisions */
.c-menu.is-open {
  max-height: 500px;
  opacity: 1;
}

.c-input.has-error {
  border-color: var(--color-danger);
}

.c-btn.is-loading {
  pointer-events: none;
  opacity: 0.6;
}
🔑 Pourquoi ça compte : les états sont le pont entre ton CSS et ton JavaScript. Une convention claire (is-, has-) fait qu'un développeur front sait immédiatement quelles classes sont manipulées dynamiquement et lesquelles sont statiques. Sans ça, chaque interaction devient une enquête.

Gap, margin, padding

Le sujet qui gâche le plus de sites. Trois outils, une hiérarchie stricte.

1. Les trois outils

OutilCe qu'il faitQuand l'utiliser
PaddingEspace à l'intérieur, entre le contenu et le bordDonner de l'air au contenu d'un composant ou d'une section
GapEspace entre les enfants, posé sur le parentLe réflexe par défaut dès qu'il y a plusieurs enfants
MarginEspace entre deux éléments, posé sur l'élémentL'exception. Un ajustement ponctuel qu'aucun des deux autres ne couvre

2. La hiérarchie de décision

1
D'abord : le gap du parent

Si plusieurs éléments doivent être espacés régulièrement, c'est le parent qui décide, une fois, pour tous. Zéro marge sur les enfants.

2
Ensuite : le padding du conteneur

Pour l'air intérieur — entre le bord d'une carte et son contenu, entre le bord d'une section et sa grille.

3
En dernier recours : la margin

Uniquement pour un cas isolé qui échappe aux deux précédents. Si tu poses des marges partout, tu as raté l'étape 1.

⛔ La règle qui sauve les sites : ne mets jamais de marge sur deux éléments voisins à la fois. Un seul des deux gère l'espace entre eux — ou mieux, aucun des deux, et c'est le gap du parent. C'est la cause n°1 des espacements incohérents (« 13px ici, 47px là ») et des marges qui fusionnent de façon imprévisible.

3. Pourquoi le gap a gagné

✗ À la marge (l'ancienne façon)
.c-card { margin-bottom: 2rem; }
.c-card:last-child {
  margin-bottom: 0;  /* la rustine */
}

Il faut toujours une exception pour le dernier. Et si l'ordre change en responsive, ça casse.

✓ Au gap (la bonne façon)
.l-grid {
  display: grid;
  gap: 2rem;
}

Le gap ne s'applique qu'entre les enfants. Jamais avant le premier, jamais après le dernier. Aucune rustine.

4. L'échelle d'espacement

Aucune valeur au hasard. Une échelle unique, et chaque espacement pointe vers un cran.

CranrempxUsage typique
--space-000Réinitialisation
--space-10.25rem4pxMicro — icône collée à son texte
--space-20.5rem8pxSerré — entre des badges
--space-30.75rem12pxUn label et son champ
--space-41rem16pxLe cran de base — le plus fréquent
--space-51.5rem24pxEntre deux blocs de texte
--space-62rem32pxEntre deux composants (cartes)
--space-83rem48pxPadding intérieur d'une section
--space-104rem64pxEntre deux grandes sections
🔑 Pourquoi une échelle : avec 9 crans fixes, toi dans six mois et un collaborateur demain tomberez naturellement sur les mêmes valeurs. Le débat « 47px ou 52px ? » n'existe plus : ces valeurs n'existent pas. C'est une contrainte, et c'est précisément ce qui produit la cohérence.

5. Et les classes u- d'espacement ?

⚠️ Position tranchée, pour éviter le chaos : dans un projet Elementor v4, les espacements structurels (padding de section, gap de container) passent toujours par les réglages natifs du Container, liés à tes variables. Les classes u-mb-4 & consorts sont réservées aux exceptions ponctuelles. Si tu utilises les deux systèmes en parallèle sans règle, tu ne sauras plus jamais d'où vient un espacement.

Nomenclature, si tu en crées malgré tout :

.u-p-4    padding sur les 4 côtés
.u-pt-4   padding-top      .u-pb-4  padding-bottom
.u-px-4   padding horizontal (left + right)
.u-py-4   padding vertical (top + bottom)

.u-m-4    margin sur les 4 côtés
.u-mt-4   margin-top       .u-mb-4  margin-bottom
.u-mx-auto  centrage horizontal d'un bloc à largeur fixe

.u-gap-4    gap uniforme
.u-gap-x-4  column-gap     .u-gap-y-4  row-gap

Typographie

La même logique d'échelle, appliquée au texte — avec les raisons UX derrière chaque valeur.

1. Taille de texte

ClassepxremUsage
.u-text-xs12px0.75remMentions légales — jamais pour du texte à lire
.u-text-sm14px0.875remLégendes, texte secondaire
.u-text-base16px1remCorps de texte — le minimum confortable
.u-text-lg18px1.125remChapô, texte d'introduction
.u-text-xl20px1.25remSous-titres
.u-text-2xl24px1.5remTitres de section (H3)
.u-text-3xl30px1.875remTitres principaux (H2)
.u-text-4xl36px2.25remTitre héros (H1)
.u-text-5xl48px3remGrande accroche
⚠️ 16px est un plancher, pas un choix esthétique. C'est la taille par défaut de tous les navigateurs, et le seuil sous lequel la lecture prolongée devient fatigante. Un corps de texte à 14px « parce que c'est plus élégant » est un choix contre l'utilisateur.

2. Graisse

ClasseValeurUsage
.u-font-light300Grands titres uniquement — illisible en petit
.u-font-regular400Corps de texte
.u-font-medium500Accent léger : labels, boutons
.u-font-semibold600Sous-titres
.u-font-bold700Titres, appels à l'action

3. Hauteur de ligne — la règle inversée

🔑 Principe fondamental : la hauteur de ligne est inversement proportionnelle à la taille du texte. Plus le texte est grand, plus le line-height doit être serré. Un H1 à 48px avec un line-height de 1.6 donne un titre qui se disloque ; à 1.1, il forme un bloc compact et lisible.
ClasseValeurPour quelle taille
.u-leading-tight1.1 – 1.2Grands titres (30px et +)
.u-leading-snug1.35Sous-titres, textes courts
.u-leading-normal1.5Corps de texte — la valeur sûre
.u-leading-relaxed1.65 – 1.75Longs paragraphes, articles

4. Espacement des lettres

.u-tracking-tight-0.02emGrands titres — les grandes lettres paraissent trop espacées, on resserre
.u-tracking-normal0Texte courant
.u-tracking-wide0.05emObligatoire sur les majuscules — sans ça, un surtitre en capitales est illisible

5. La typographie fluide (clamp)

Pour les titres, oublie les tailles fixes par breakpoint. Un titre doit grandir continûment avec l'écran, pas par sauts brutaux.

/* Un H1 qui passe de 32px (mobile) à 56px (desktop),
   de façon fluide, sans une seule media query */
--font-size-h1: clamp(2rem, 1.2rem + 3.5vw, 3.5rem);
🔑 Pourquoi c'est central dans ton workflow : c'est exactement ce que Figma ne sait pas représenter nativement. C'est aussi ce qui distingue un site « adapté au mobile » d'un site réellement fluide. Passerelle génère ces valeurs pour toi.

6. Hiérarchie de titres : pas des classes u-

⚠️ Erreur fréquente : créer .u-text-4xl et l'appliquer à la main sur chaque H1. Non. La hiérarchie H1→H6 d'un site se définit une fois dans les Réglages du site → Typographie d'Elementor. Les classes u-text-* servent aux exceptions — un chiffre-clé mis en avant, un chapô plus grand. Pas à la structure du document.

Cascade & spécificité

« Pourquoi ma classe ne s'applique pas ?! » — la frustration n°1, et sa résolution définitive.

💡 Le C de CSS veut dire « Cascading ». Ce n'est pas un détail : c'est le cœur du langage. Quand deux règles se contredisent, il faut un arbitre. Cet arbitre s'appelle la spécificité, et il obéit à des règles strictes — pas à ton intuition.

1. L'ordre de priorité (du plus faible au plus fort)

RangType de règleExemplePoids
1Sélecteur de baliseh1 { }0-0-1
2Classe.c-title { }0-1-0
3Deux classes.c-card .c-title { }0-2-0
4ID#header { }1-0-0
5Style inlinestyle="..."1-0-0-0
6!importantcolor: red !important;Écrase presque tout

La lecture du « poids » : on compare de gauche à droite. Un seul ID bat n'importe quel nombre de classes. C'est pour ça qu'on évite les ID en CSS — ils sont impossibles à surcharger proprement.

2. À égalité, le dernier gagne

.c-btn { background: blue; }
.c-btn { background: green; }   ← c'est celle-ci qui s'applique

Deux règles de même spécificité ? Celle qui arrive plus bas dans la feuille de style l'emporte. D'où l'importance de l'ordre de chargement de tes fichiers CSS.

3. Le cas Elementor v4

C'est là que ça devient concret. Dans Elementor v4, plusieurs sources de style cohabitent sur un même élément :

SourcePrioritéRemarque
Réglages globaux du site (typographie, couleurs)La plus faibleLa base sur laquelle tout se construit
Classe globale (.c-btn)MoyenneCe que tu crées dans le Class Manager
Classe locale de l'élémentPlus forteGénérée automatiquement par Elementor quand tu stylises un widget directement
CSS personnalisé de l'élémentLa plus forteÀ réserver aux cas vraiment particuliers
⛔ La cause n°1 du « ma classe globale ne marche pas » : tu as déjà stylisé cet élément directement dans l'onglet Style d'Elementor. Ça a créé une classe locale, plus spécifique, qui écrase ta classe globale. Solution : réinitialise le style local de l'élément (bouton « reset » sur le contrôle), et laisse la classe globale faire son travail.

4. !important : le symptôme, pas la solution

Quand tu écris !important, tu ne résous pas un problème de style. Tu résous un problème d'architecture — mal.

✗ La rustine
.c-btn {
  background: red !important;
}

Ça marche aujourd'hui. Demain, quelqu'un devra écrire un autre !important pour te surcharger. Puis un troisième. C'est une escalade sans fin.

✓ Le diagnostic

Demande-toi pourquoi ta règle est écrasée. Un style local Elementor ? Un ID ? Un sélecteur trop imbriqué ? Enlève la cause plutôt que de la contourner.

5. Règle d'or : garde la spécificité basse et plate

🔑 C'est précisément ce que BEM apporte. En nommant .c-card__title plutôt qu'en écrivant .c-card .content .header h3, tu obtiens le même ciblage avec une spécificité de 0-1-0 au lieu de 0-3-1. Résultat : tes règles restent faciles à surcharger quand tu en as besoin, sans jamais recourir à !important. La nomenclature n'est pas qu'une question de lisibilité — c'est un outil de contrôle de la spécificité.

Anti-patterns

Ce qui trahit un débutant. On apprend plus vite de ce qui casse que de ce qui marche.

1Nommer l'apparence

.c-btn-blue · .u-text-red · .c-box-shadow-big

Pourquoi c'est cassé : le jour où le client passe au vert, tu as deux options — garder une classe blue qui affiche du vert (un mensonge inscrit dans le code), ou renommer 40 occurrences. Les deux sont mauvaises.

La correction : .c-btn--primary. Le rôle ne change pas, même si la couleur change.

2Nommer la position

.c-left-box · .l-right-column · .c-top-banner

Pourquoi c'est cassé : en responsive, ta colonne de droite passe en dessous. Elle s'appelle toujours right. Le nom devient un piège pour la personne suivante.

La correction : nomme la fonction.l-sidebar, .c-banner--promo.

3La soupe d'utilitaires

class="u-p-4 u-mb-6 u-bg-white u-rounded u-shadow u-flex u-gap-3"

Pourquoi c'est cassé : tu as réinventé le style inline, en plus long. Ce bloc se répète sur 12 pages ; le jour où le padding change, tu édites 12 pages. Tu n'as gagné aucune réutilisabilité.

La correction : au-delà de 3 utilitaires empilés qui se répètent, nomme le motif. C'est un composant.

4Les noms numérotés (le refus de refactorer)

.c-card-2 · .c-card-new · .c-card-final · .c-card-final-v2

Pourquoi c'est cassé : ces noms n'existent que parce qu'on n'a pas osé toucher à l'original. Personne ne saura jamais laquelle est la bonne. C'est de la dette pure.

La correction : si c'est une variation → .c-card--compact. Si l'ancienne est morte → supprime-la (voir Cycle de vie).

5!important par réflexe

Pourquoi c'est cassé : c'est un symptôme, pas une solution. Il signale que ton architecture de spécificité est cassée quelque part. Chaque !important en appelle un autre pour le surcharger. L'escalade est sans fin.

La correction : trouve pourquoi ta règle est écrasée (style local Elementor ? ID ? imbrication ?) et supprime la cause.

6Le composant qui se positionne

.c-card { margin-bottom: 2rem; width: 300px; }

Pourquoi c'est cassé : ta carte n'est plus réutilisable. Dans une grille qui gère déjà son gap, elle ajoute une marge parasite. Dans un contexte plus étroit, sa largeur fixe déborde.

La correction : le composant gère son intérieur. Le parent gère son extérieur.

7Le chaînage BEM

.c-card__body__header__title

Pourquoi c'est cassé : BEM est plat. Tu viens d'inscrire la structure du DOM dans un nom de classe. Le jour où tu supprimes le body, le nom ment.

La correction : .c-card__title. Peu importe la profondeur, l'élément appartient au bloc, point.

8Doubler une fonctionnalité native

Créer .u-gap-4 alors que le Container Elementor a un réglage Gap natif.

Pourquoi c'est cassé : deux sources de vérité pour la même chose. Dans six mois, tu ne sauras plus si l'espacement d'une section vient du réglage natif ou d'une classe. Le débogage devient un cauchemar.

La correction : une seule source par type de décision. Structurel → natif. Exception → classe.

9L'état dans le nom du bloc

.c-menu--open au lieu de .c-menu.is-open

Pourquoi c'est cassé : tu devras créer un --open pour chaque composant qui s'ouvre. Et ton JavaScript devra connaître le nom du composant pour poser la bonne classe.

La correction : .is-open, une seule classe universelle, posée par le JS sur n'importe quel composant.

🔑 Le fil rouge de ces 9 erreurs : elles viennent toutes de la même racine — décrire ce qu'on voit plutôt que ce qui est. L'apparence, la position, le DOM, l'ordre chronologique. Tout ça change. Le rôle, lui, reste.

Cas pratique commenté

Une section réelle, du brief au code — avec chaque décision expliquée à voix haute. Les taxonomies s'oublient ; un raisonnement se retient.

Le brief : une section « Nos services » avec un titre, un chapô, et trois cartes (icône, titre, description, lien). Sur desktop, les cartes sont côte à côte. Sur mobile, elles s'empilent. Une des trois cartes est mise en avant.

Étape 1 — Je résiste à l'envie de nommer

Premier réflexe du débutant : ouvrir le Class Manager et créer .services-section, .services-title, .services-grid, .service-card… Stop.

Question : cette section « Nos services » existe-t-elle ailleurs sur le site ? Non, elle est unique à la page d'accueil.

Décision : je ne crée aucune classe spécifique à « services ». Une classe utilisée une fois est un coût sans contrepartie. Je vais chercher les motifs génériques qui se cachent derrière.

Étape 2 — Je cherche les motifs réutilisables

Je regarde la maquette et je me demande : qu'est-ce que je reverrai ailleurs sur ce site ?

🔑 Le déclic : j'ai transformé une section unique en assemblage de motifs génériques. Aucun de mes noms ne contient le mot « services ». C'est exactement ce qu'il faut : demain, la même structure servira pour « Nos réalisations » sans que je change une ligne.

Étape 3 — Le titre et le chapô : je ne nomme rien

Question : ai-je besoin d'une classe .c-section-title ?

Réponse : non. Un H2 est un H2. Sa taille, sa graisse et sa couleur sont déjà définies dans les Réglages du site → Typographie d'Elementor. Créer une classe pour ça, ce serait doubler une fonctionnalité native — l'anti-pattern n°8.

Le chapô, lui, est un paragraphe légèrement plus grand que le corps. Une exception ponctuelle → une classe utilitaire suffit : .u-text-lg. Pas de composant.

Étape 4 — L'espacement : je ne pose aucune marge

Le piège : mettre margin-bottom: 2rem sur le titre, puis sur le chapô, puis sur chaque carte.

Ce que je fais à la place : le container de la section est un l-stack avec un gap. Il espace automatiquement le titre, le chapô et la grille. La grille, elle, a son propre gap entre les cartes.

Résultat : zéro marge dans tout le bloc. Deux valeurs de gap, posées sur deux parents. Si le client trouve que « ça manque d'air », je change une valeur.

Étape 5 — La carte mise en avant : variation, pas nouveau composant

Question : est-ce que je crée .c-featured-card ?

Non. C'est la même carte, avec un fond différent. Ce n'est pas un autre objet, c'est une sorte de carte. Donc : une variation BEM → .c-card--featured.

Et si le client veut plus tard la mettre en avant dynamiquement au survol ? Là, ce serait un état.is-highlighted. La distinction : la variation est décidée au design, l'état survient à l'exécution.

Le résultat

<section class="l-section">
  <div class="l-container l-stack">

    <h2>Nos services</h2>
    <p class="u-text-lg">Une phrase d'introduction.</p>

    <div class="l-grid">

      <article class="c-card">
        <div class="c-card__icon">...</div>
        <h3 class="c-card__title">Design</h3>
        <p class="c-card__text">Description.</p>
        <a class="c-btn c-btn--ghost">En savoir plus</a>
      </article>

      <article class="c-card c-card--featured">
        ... même structure ...
      </article>

      <article class="c-card">
        ... même structure ...
      </article>

    </div>
  </div>
</section>

Le bilan

Ce que j'ai crééRéutilisable ailleurs ?
l-section, l-container, l-stack, l-gridSur toutes les sections du site
c-card + ses éléments + --featuredPortfolio, blog, témoignages…
c-btn + --ghostPartout
u-text-lgChaque chapô du site

Aucune classe ne mentionne « services ». J'ai construit une section unique avec un vocabulaire entièrement générique. C'est la marque d'un design system qui tient.

🔑 La leçon la plus importante de cette page : le bon réflexe n'est pas « comment je nomme cette section ? », c'est « quels motifs génériques se cachent dans cette section ? ». Tu ne construis pas une page. Tu assembles un vocabulaire.

Fondements UX

Pourquoi les échelles ne sont pas des chiffres au hasard. Chaque valeur répond à une contrainte de perception humaine.

💡 Sans cette page, tes échelles ressemblent à des nombres arbitraires. Avec elle, tu comprends que ce sont des contraintes de lisibilité déguisées en tokens — et tu sauras les défendre face à un client qui veut « du texte plus petit, c'est plus élégant ».

1. Longueur de ligne : 60 à 75 caractères

Au-delà de ~75 caractères par ligne, l'œil peine à retrouver le début de la ligne suivante. En dessous de ~45, le rythme de lecture est haché par les retours à la ligne trop fréquents.

/* La façon la plus simple de respecter ça */
.l-prose {
  max-width: 65ch;   /* 65 caractères, littéralement */
}

Conséquence sur ta nomenclature : un l-container à 1200px, c'est bien pour une grille de cartes. C'est beaucoup trop large pour un article. D'où l'intérêt d'un layout dédié au texte long.

2. Zone tactile : 44 × 44 px minimum

C'est la taille moyenne de la pulpe d'un doigt. En dessous, le taux d'erreur de tap grimpe en flèche. Un bouton de 30px de haut sur mobile est un bouton qu'on rate.

⚠️ Le piège classique : les liens de navigation en texte, ou les petites icônes (fermer, partager). Visuellement, l'icône fait 20px. Sa zone cliquable doit quand même faire 44px — par du padding, pas par une icône plus grosse.
.c-btn-icon {
  width: 44px;
  height: 44px;      /* la zone tactile */
  display: flex;
  align-items: center;
  justify-content: center;
}
.c-btn-icon svg {
  width: 20px;       /* l'icône visible */
}

3. Contraste : 4.5:1 minimum

ContexteRatio minimumNorme
Texte courant (moins de 18px)4.5 : 1WCAG AA
Grand texte (18px+ gras, ou 24px+)3 : 1WCAG AA
Éléments d'interface (bordures de champs, icônes)3 : 1WCAG AA
Idéal, confort maximum7 : 1WCAG AAA

Conséquence sur tes tokens : le gris clair « élégant » à #aaa sur fond blanc donne un ratio de 2.3:1. Il est illisible pour une partie de tes utilisateurs. Un token --color-text-muted doit être testé, pas choisi à l'œil.

4. La hiérarchie visuelle a besoin d'écarts francs

Un H2 à 20px et un corps à 18px : l'œil ne perçoit pas la différence. La hiérarchie n'existe pas.

🔑 Règle pratique : entre deux niveaux de titre, vise un écart d'au moins 1.25×. C'est pour ça que les échelles typographiques progressent par ratios (16 → 20 → 24 → 30 → 36) et non par pas constants (16 → 18 → 20 → 22). Un pas constant produit une hiérarchie molle.

5. La loi de proximité (Gestalt)

Deux éléments proches sont perçus comme liés. Deux éléments éloignés, comme séparés. C'est automatique et pré-conscient.

La conséquence, terriblement fréquente : un titre de section qui a autant d'espace au-dessus qu'en dessous. L'œil ne sait plus s'il appartient au bloc précédent ou au suivant.

✗ Espacement symétrique

Titre avec 32px au-dessus et 32px en dessous. L'œil hésite : ce titre annonce quoi ?

✓ Espacement asymétrique

64px au-dessus, 16px en dessous. Le titre « colle » visuellement à ce qu'il introduit. Aucune ambiguïté.

Conséquence sur ta nomenclature : ton échelle d'espacement doit avoir assez de crans pour exprimer cette asymétrie. C'est aussi pourquoi le gap uniforme d'un l-stack ne suffit pas toujours — parfois, un titre a besoin de sa propre relation d'espacement avec ce qui suit.

6. La cohérence bat l'optimalité

🔑 La vérité qui dérange : un système imparfait mais cohérent produit une meilleure expérience qu'un ensemble de décisions individuellement parfaites mais incohérentes. L'utilisateur ne mesure pas tes padding — il ressent le rythme. Un rythme régulier, même perfectible, se lit comme de la qualité. Un rythme cassé se lit comme de l'amateurisme, même si chaque valeur prise isolément était « la bonne ».

C'est la justification profonde de tout ce document : la nomenclature et les échelles ne servent pas à trouver la valeur parfaite. Elles servent à garantir que tu réutilises la même valeur.

Cycle de vie

Un design system n'est pas figé. Sans règles d'entretien, il pourrit en 18 mois.

1. Le symptôme d'un système qui pourrit

⛔ Le dernier point est le plus grave. Quand créer coûte moins cher que chercher, ton système est mort. Il ne reste qu'un tas de CSS.

2. Auditer : les classes orphelines

Le Class Manager d'Elementor v4 affiche le nombre d'utilisations de chaque classe — et où. Sers-t'en.

Nombre d'usagesDiagnosticAction
0Orpheline — vestige d'une idée abandonnéeSupprime, sans état d'âme
1Ne mérite pas d'être globale (règle de trois)Remets le style en local, supprime la classe
2Zone grise — surveilleGarde si une 3e occurrence est prévisible
3+Le système fonctionneRien à faire

Rythme recommandé : un audit à chaque fin de projet, avant la livraison. C'est le seul moment où tu as encore tout le contexte en tête.

3. Renommer une classe utilisée 40 fois

Ça arrive : tu as nommé .c-btn-blue au début du projet, tu as compris ton erreur. Comment corriger sans tout casser ?

1
Crée le nouveau nom, en parallèle

.c-btn--primary existe désormais à côté de .c-btn-blue. Les deux ont le même style. Rien n'est cassé.

2
Migre progressivement

À chaque page que tu touches pour une autre raison, remplace l'ancienne classe par la nouvelle. Pas de « grand soir » de refactoring.

3
Surveille le compteur

Le Class Manager te dit combien d'occurrences de l'ancien nom subsistent. Quand il tombe à zéro…

4
Supprime l'ancienne

Le compteur à 0 est ta garantie que rien ne casse. C'est exactement à ça que sert le Usage Count.

4. Déprécier plutôt que supprimer brutalement

Sur un projet livré à un client qui édite lui-même ses pages, une suppression brutale peut casser une page que tu n'as pas vue. Marque d'abord, supprime ensuite :

/* DÉPRÉCIÉ — remplacé par .c-btn--primary
   À supprimer après migration complète (audit prévu : mars) */
.c-btn-blue { ... }

5. La règle du « ajouter coûte plus cher que réutiliser »

🔑 Le test de santé de ton système : quand tu commences une nouvelle page, ton premier réflexe est-il de chercher dans les classes existantes, ou d'en créer une nouvelle ? Si c'est créer, c'est que chercher est trop pénible — noms peu clairs, trop de classes, pas de documentation. Le problème n'est pas la discipline de l'utilisateur : c'est la conception du système.

6. Le kit qui voyage entre projets

C'est le cœur de la reproductibilité : ta nomenclature (c-btn, c-card, l-section, l'échelle d'espacement) reste identique d'un client à l'autre. Seules les valeurs changent — la couleur primaire, la police, éventuellement le rythme d'espacement.

Elementor v4 permet d'exporter et réimporter une collection complète de classes et variables. Concrètement, ton point de départ sur un nouveau projet n'est jamais une page blanche : c'est ton kit, dont tu échanges les valeurs de marque.

🔑 C'est là que se trouve la vraie valeur d'un design system personnel : pas dans l'originalité de la nomenclature (elle doit être ennuyeuse et stable), mais dans le fait que le squelette est déjà là, invisible, à chaque nouveau projet. Tu ne redécides plus jamais comment nommer un bouton. Tu conçois.

Dans Elementor v4

Où poser chaque brique, concrètement, dans l'interface.

Le bon outil pour chaque décision

Ce que tu veux définirOù, dans Elementor v4Surtout pas
Couleurs de marque, échelle d'espacement, taillesVariables ManagerDes valeurs en dur dans chaque widget
Hiérarchie de titres H1→H6Réglages du site → TypographieUne classe .u-text-4xl posée à la main
Espacement entre les enfants d'un containerRéglage Gap natif du ContainerUne marge sur chaque enfant
Un motif visuel répété 3+ foisClass Manager — classe globaleCopier-coller le style sur chaque instance
Un style utilisé une seule foisStyle local de l'élémentUne classe globale (dette inutile)
Un bloc structurel complet réutiliséComponent (v4) — synchronisé globalementDupliquer la section à la main

La règle qui évite le chaos

⛔ Une seule source de vérité par type de décision. Si l'espacement d'une section peut venir soit du réglage Gap natif soit d'une classe u-gap-4, tu ne sauras plus jamais d'où il vient. Choisis, écris-le dans ta doc de projet, et tiens-toi-y.

Pourquoi le CSS-first change tout

En v4, le style d'un élément est porté par des classes globales réutilisables plutôt que par des réglages isolés sur chaque widget. Conséquences directes :

Containers Flexbox : le socle

Les Containers génèrent un DOM nettement plus léger que l'ancien système Section → Column (2 niveaux au lieu de 4), et exposent nativement le gap. Pour tout nouveau projet : Containers, sans hésiter.

Checklist de démarrage projet

  1. Importe ton kit de classes et variables du projet précédent
  2. Change uniquement les valeurs de marque (couleurs, police) dans le Variables Manager
  3. Définis la hiérarchie typographique dans les Réglages du site
  4. Construis avec les Containers, en utilisant le gap natif
  5. Ne crée une nouvelle classe que si un motif se répète 3 fois
  6. Avant livraison : audite les orphelines dans le Class Manager

Et Figma dans tout ça ?

Une fois cette nomenclature stable côté Elementor, le passage à Figma est presque mécanique :

⚠️ La seule vraie limite : le clamp() n'a pas d'équivalent natif dans Figma. Une valeur fluide (« 32px sur mobile, 56px sur desktop, en continu ») ne peut être représentée — seulement annotée. C'est la frontière irréductible entre les deux outils, et la raison pour laquelle la maquette Figma ne sera jamais la source de vérité absolue du responsive.

Glossaire

Tout le vocabulaire de ce document, en français simple.

Nomenclature

Composant (c-)Objet autonome et réutilisable. Gère son intérieur, jamais sa position.
Layout (l-)Structure qui positionne les composants. Gère l'espace, pas l'apparence.
Utilitaire (u-)Une classe, une propriété. Au-delà de 3 empilés, c'est un composant qui s'ignore.
État (is- / has-)Condition temporaire, posée par le JavaScript. is-open, has-error.
BEMBloc / Élément / Modificateur. .c-card__title--big. Toujours plat, jamais chaîné.
Variation (--)Une sorte permanente de composant. À distinguer d'un état.
Règle de troisOn ne nomme qu'à la 3e occurrence. Avant, c'est peut-être une coïncidence.

CSS

PaddingEspace intérieur, entre le contenu et le bord.
MarginEspace extérieur, entre deux éléments. L'exception, pas la règle.
GapEspace entre les enfants d'un flex/grid, posé sur le parent. Le réflexe par défaut.
CascadeLe mécanisme qui arbitre entre deux règles contradictoires.
SpécificitéLe « poids » d'un sélecteur. Un ID bat toutes les classes. À garder basse et plate.
!importantUn symptôme d'architecture cassée, jamais une solution.
clamp()Fait varier une valeur en continu entre deux tailles d'écran. Sans media query.
ÉchelleSérie de valeurs fixes. Sa fonction n'est pas d'être parfaite, mais d'être réutilisée.
chUnité = largeur du caractère « 0 ». max-width: 65ch = ligne idéale de lecture.

Elementor v4

Variables ManagerPanneau centralisant couleurs, tailles, espacements. Ta source de vérité.
Class ManagerPanneau des classes globales. Affiche où et combien de fois chacune est utilisée.
Classe globaleRéutilisable sur tout le site. Une modification se propage partout.
Classe localeGénérée automatiquement quand tu stylises un widget directement. Écrase la classe globale.
ContainerLe Flexbox Container. C'est ton layout natif — utilise son gap.
ComponentUn bloc structurel réutilisable et synchronisé globalement.

UX

65chLongueur de ligne idéale pour la lecture. Au-delà de 75, l'œil se perd.
44 × 44pxZone tactile minimum sur mobile. Le padding compte, pas la taille de l'icône.
4.5:1Contraste minimum pour du texte courant (WCAG AA).
Loi de proximitéDeux éléments proches sont perçus comme liés. D'où l'espacement asymétrique des titres.