Il existe de nombreuses manières de rédiger du code CSS. Y compris, soyons honnêtes, celle très répandue consistant à dire « le plus important, c’est que ça marche et le reste on s’en fiche ».

Mais imaginez un instant que vous deviez apporter des modifications dans un projet déjà existant et que vous tombiez dans un CSS sur quelque chose de ce genre…

body > div.content .row div.article .title > p span > a {  } 

Il est clair qu’il n’y a rien de bon à attendre de la suite… et que vous allez devoir passer beaucoup de temps avant de pouvoir apporter les modifications nécessaires sans pour autant ne pas apporter de régression dans l’existant.

Et lorsqu’il s’agit d’un gros projet nécessitant un support et des modifications en permanence, alors, tôt ou tard on commence à perdre le contrôle sur le code. On voit bien qu’il ne sera pas possible de bien coder sans avoir une approche systématique. Il existe de nombreuses normes reconnues dans le monde du web, et aujourd’hui, je vous propose d’aborder la méthodologie BEM.

BEM est une méthodologie de développement web ainsi qu’un ensemble de bibliothèques d’interface, de frameworks et d’outils complémentaires qui ont été élaborées par Yandex, le moteur de recherche russe. Son histoire a commencé en 2005.

Les concepts BEM apportent non seulement une convention de nommage des sélecteurs, mais également une vision du projet constitué par un ensemble d’entités (des blocs, des éléments, des modificateurs).

Il faut cependant souligner que BEM est aujourd’hui une méthode très largement reconnue dans le monde entier. Il suffit de jeter un œil aux entreprises qui l’utilisent : Google, BBC, Buzzfeed et beaucoup d’autres encore.

 

Pourquoi cette méthodologie a-t-elle été créée ?

Afin d’accélérer le processus de développement, il était indispensable de faciliter le support HTML et CSS des différents éléments de la page et de rendre le code de ces éléments plus indépendant.

Pour ce faire, la page a été divisée en différentes sections. C’est ainsi qu’est apparu le nouveau concept de bloc. Un bloc pouvait être constitué de divers éléments qui ne pouvaient pas être utilisés en dehors de ce bloc. Les états et le comportement d’un bloc et d’un élément pouvaient être spécifiés à l’aide d’un modificateur.

Ce sont autour de ces trois concepts clés que la majorité des règles furent créées. Et c’est ainsi que la méthodologie fut nommée selon l’acronyme reprenant la première lettre de ces trois concepts clés – Bloc, Elément et Modificateur.

Nous allons maintenant aller plus en détails dans chacun de ces concepts.

Le bloc

C’est un composant de page logiquement et fonctionnellement indépendant. Il est entièrement autonome et peut avoir son propre contexte : comportement, modèles, styles, documentation, etc. Les blocs peuvent être utilisés et réutilisés n’importe où sur une page et même dans un autre projet.

Certains blocs peuvent être imbriqués dans d’autres blocs, combinés ou utilisés pour créer d’autres blocs plus complexes.

L’élément

C’est une partie constitutive d’un bloc qui ne peut être utilisée en dehors de celui-ci et qui n’a d’existence que dans le cadre de son parent. Les éléments ne sont pas des composants obligatoires du bloc : très souvent, des petits blocs ne contiennent aucun élément.

Le modificateur

C’est une propriété d’un bloc ou d’un élément qui modifie leur apparence, leur état ou leur comportement.

Un modificateur possède un nom et peut avoir une valeur. L’utilisation des modificateurs est optionnelle. Chaque bloc et chaque élément peuvent avoir plusieurs modificateurs différents en même temps.

Pourquoi utiliser BEM ?

  • Utilisation de nom « parlant » pour les classes du code(auto-documentation).
  • Création d’espaces de noms
  • Indépendance des blocs. Cela signifie que le fonctionnement du code est prévisible et qu’il n’y a pas de croisements de noms.
  • Maintenance du code (modification, expansion) — simple et pas dangereuse.
  • Possibilité de créer ses propres bibliothèques de composants.
  • Aucune dépendance vis-à-vis de la structure du DOM
  • Réutilisation de blocs à travers un projet
  • Réutilisation de blocs dans différents projets

 

Les problèmes résolus par BEM

Problème N°1. Le composant est dépendant de son parent.

Il est relativement difficile, lorsqu’on lit du code CSS, de voir quel bloc dépend de quel autre. Cela résulte en une certaine imprévisibilité du comportement.

Partout vous avez :

.selector {color: red;}

Mais tout à coup, vous trouvez un :

.parent .selector {color: red;}

Et si, après, vous devez faire ce changement :

.selector {color: blue;}

Cela va vous prendre un temps considérable pour comprendre pourquoi la couleur n’a pas été modifiée partout. Et maintenant, imaginez un sélecteur constitué de 4 parties…

Dans BEM, il n’y a presque pas de cascade de ce type.

Problème N°2. Les sélecteurs complexes et spécifiques.

Plus le sélecteur est complexe ou spécifique, plus il est lié avec la structure de DOM.

Imaginons le menu déroulant suivant :

#nav ul li ul li { }

Et si vous avez besoin de réutiliser ce menu, mais cette fois-ci sans le parent #nav ?

Dans BEM, cette situation est Impossible. Seules les classes sont utilisées en qualité de sélecteur (pas d’id et pas de balise) et presque pas de cascade.

Problème N°3. Il y a trop de noms de classes généraux.

Utiliser des noms de classes généraux peut engendrer des répétitions et des héritages de style pas très évidents.


<div class="block">
    <h3 class="title">Titre</h3>
    <div class="contents">Contenu</div>
</div>
<style>
    .block {}
    .block .title{}
    .block .contents{}
</style>

Peut-on être certain qu’en aucun autre endroit, nous n’aurons pas :

.title{}
.contents{}

La superposition de styles peut provoquer de nombreux problèmes. Or, en BEM, chaque nom de sélecteur est unique.

Problème N°4. CSS est incompréhensible sans contexte (HTML).

Même si aujourd’hui vous êtes le seul développeur sur un projet donné, cela ne signifie pas qu’après vous aucune autre personne ne va intervenir sur ce code. D’autant plus, que vous pouvez vous aussi revenir sur ce projet beaucoup plus tard, et ne rien vous rappeler à son sujet.

.SbcPstW {..} /*Qu’est-ce que c’est ?*/.
/* C’est un conteneur de bas de page pour un article de blog  */
/* En utilisant BEM: */
.post {...}
...
.post__footer-wrap {...}
.post__footer-date {...}
.post__footer-tags {...}

Dans BEM, chaque nom de sélecteur indique son rôle dans HTML.

 

Bon à savoir

La méthodologie BEM implique que les blocs ne sont pas uniques et qu’ils peuvent toujours être réutilisés. C’est pour cette raison que dans la description des règles CSS, les attributs id ne sont pas utilisés.

Un bloc ne doit pas être dépendant des autres blocs autour de lui, et il ne doit pas affecter les blocs adjacents. Aussi, dans CSS, on n’utilise plus :

  • les balises ;
  • les sélecteurs imbriqués.

Règles de nommage des entités BEM

  • Chaque entité BEM doit avoir sa propre classe
  • Les propriétés CSS pour les blocs, les éléments et les modificateurs sont décrites uniquement dans les classes
  • Pour séparer les mots dans un nom, on utilise un trait d’union (-)
  • Pour séparer le nom du bloc et le nom de l’élément, on utilise deux tirets bas consécutifs (__)

<!-- .BEM-bloc -->.
<div class="tabbed-pane">
    <!-- .BEM-element -->.
    <div class="tabbed-pane__panel">...</div>
</div>
  • Selon la règle de Yandex, pour séparer le nom du bloc et le nom du modificateur, on utilise un seul tiret bas (_)

<div class="header header_christmas">...</div><!-- Christmas edition of the header -->
  • Harry Roberts, quant à lui, a proposé d’utiliser deux traits d’union (–)

<div class="header header--christmas">...</div><!-- Christmas edition of the header -->

Nous conseillons en général d’utiliser cette dernière car elle est plus claire et permet en un coup d’œil de bien voir la structure du code.

 

Conseils pratiques d’utilisation

Un bloc est un élément de page indépendant

  • Le nom de la classe doit être simple et court
  • Le nom de la classe doit répondre à la question : « Qu’est-ce que c’est ? »
  • Ne pas utiliser d’abréviations, sauf les plus courantes (button – btn, additional – add, description – descr, image – img ; picture – pic, etc.)
  • Le nom ne doit pas répondre à la question « Quelle est son apparence ? »

<!-- .BEM-block -->.
<div class="product">
    …
</div>

Les blocs peuvent et doivent être imbriqués les uns dans les autres

  • Il n’y a aucune restriction concernant l’imbrication de blocs, tant que nous ne tombons pas dans l’absurde
  • Les blocs imbriqués n’ont aucune propriété spécifique

<!-- .page-header — bloc BEM -->.
<div class="page-header">
    <!-- .logo — bloc BEM à l’intérieur d’un bloc BEM -->.
    <a class="logo"></a>
</div>

Certains composants d’une page sont toujours des blocs BEM

Tous les projets (mis à part ceux qui sont très petits ou qui ont une probabilité de modification proche de zéro) comprennent des composants qui, en raison de leur forte probabilité de réutilisation, sont toujours et judicieusement définis comme des blocs BEM.

Tout web designer professionnel utilise des composants existants pour construire de nouvelles pages.

  • Bouton
  • Blocs dans des formulaires (bloc de saisie de texte, bloc de choix, etc.)
  • Pagination
  • Tabs
  • Labels
  • Liens vers les réseaux sociaux
  • Articles dans des blogs
  • Produits dans un catalogue
  • Carrousel, etc.

L’élément est une partie du bloc BEM

  • Le nom de la classe est généré à partir du nom du bloc séparé du nom de l’élément par deux tirets bas (__)
  • Le nom de la classe doit être simple et court
  • Le nom de la classe doit répondre à la question :« Qu’est-ce que c’est ? »
  • Ne pas utiliser d’abréviations,sauf les plus courantes (button – btn, additional – add, description – descr, image – img ; picture – pic etc.)
  • Le nom ne doit pas répondre à la question « Quelle est son apparence ? »

<!-- . bloc BEM -->.
<div class="product">
    <!-- .product__image — élément BEM du bloc BEM product-->.
    <img class="product__image">
    <!-- .product__description — élément BEM du bloc BEM product-->.
    <p class="product__description">…</p>
    <!-- .product__more-link — BEM élément BEM du bloc BEM product-->.
    <a class="product__more-link" href="#">…</a>
</div>

L’élément ne doit pas être utilisé en dehors du bloc puisque

  • On perd la logique du code
  • Il est difficile de donner un style à des éléments qui peuvent se trouver à n’importe quel endroit (float, positionnement absolu et d’autres styles de cet élément peuvent « casser » la mise en page)

Il peut ne pas y avoir d’éléments

Tous les blocs ne doivent pas forcément avoir d’éléments : un bouton, par exemple,est toujours un bloc BEM, mais il est relativement rare qu’il contienne des éléments.

<!-- .Pas d’erreur : bloc sans éléments  -->.
<button class="common-btn">Télécharger</button>

Comment distinguer un bloc BEM et un élément BEM ?

Posez-vous tout simplement la question suivante : « Puis-je avoir besoin de cette entité séparément, de manière indépendante ? Ou n’aurai-je besoin d’elle qu’à l’intérieur de son parent ? » Si vous en avez besoin séparément, il s’agit alors d’un bloc BEM. Si elle n’a de sens qu’à l’intérieur de son parent, c’est un élément BEM.

En cas de doute, optez plutôt pour le bloc BEM.

Le mix est la combinaison d’un bloc BEM et d’un élément BEM sur le même nœud de DOM

N’oubliez pas que vous avez la possibilité de combiner sur une même balise aussi bien une classe de niveau élément BEM d’un bloc parent que sa propre classe du bloc BEM. Les classes de blocs BEM doivent être écrites en premier.

Il faut noter cependant qu’avec cette approche le code devient moins lisible.

Prenons un exemple.

Dans un projet, les boutons sont réalisés grâce au bloc button. Vous devez le placer dans la zone de recherche (search-form) et définir les marges du bouton. A cet effet, nous utilisons un mix du bloc button et de l’élément button du bloc search-form :


<div class="search-form">
    <button class="button search-form__button">Search</button>
</div>

Le mix nous permet d’utiliser un bouton universel qui ne sait rien des marges par rapport aux limites du formulaire concerné. Ici, le formulaire de recherche se trouve être l’élément search-form__button, qui sait où il doit se trouver ainsi que le bloc button qu’il faut afficher.

L’arborescence de BEM est plate à la différence de DOM

Dans les classes des éléments BEM, il est impossible de définir une hiérarchie, l’utilisation de deux fois ou plus deux caractères ‘__’ est interdite (un seul niveau).


<div class="promo">
    <div class="promo__description">…</div>
    <!-- . Il n’y a pas d’erreur  -->.
    <a class="promo__link" href="#">…</a>
</div>

<div class="promo">
    <div class="promo__description">…</div>
    <!-- . Il y une erreur. Tentative de création d’une hiérarchie  -->.
    <a class="promo__description__link" href="#">…</a>
</div>

Le modificateur est une classe supplémentaire pour changer l’apparence ou le comportement d’un bloc ou d’un élément

  • Le nom de la classe est généré à partir du nom du bloc (ou de l’élément) séparé du nom du modificateur par deux trait d’union (–)
  • Le nom de la classe doit être simple et court
  • Le nom de la classe doit répondre aux questions :« Qu’est-ce que c’est ? », « Qu’est-ce qu’il modifie ? », « Qu’est-ce qui différencie cette classe des autres ? »
  • Évitez d’utiliser des abréviations, sauf les plus courantes

<!-- . product—large est un BEM modificateur-->.
<div class="product product--large">
    …
</div>

Un modificateur ne peut pas être utilisé de manière isolée

La classe d’un modificateur ne doit jamais être utilisée de manière isolée, mais toujours et seulement avec la classe qu’il modifie.


<!--  Il n’y a pas d’erreur. Le modificateur va avec la classe qu’il modifie    -->.
<div class="product product--large">
    …
</div>
<!--  Il y une erreur. Modificateur sans la classe qu’il modifie    -->.
<div class=" product--large">
    …
</div>

Les imbrications de sélecteurs

  • Moins il y a de niveaux, mieux c’est
  • Il ne faut pas utiliser plus de 3 niveaux hiérarchiques (on ne compte pas les pseudo-éléments, les pseudo-sélecteurs et les MediasQueries)
  • Il faut utiliser avec parcimonie les sélecteurs d’éléments fils (>) ou sélecteurs de voisins (+)
  • Laissez toujours une ligne blanche avant le sélecteur imbriqué ou @media
  • Faites toujours une indentation supplémentaire pour les imbrications

Esperluette (&)

  • Il faut utiliser l’esperluetteseulement dans les cas suivants :
    • devant un séparateur d’élément BEM
    • devant un séparateur de modificateur BEM
    • devantun pseudo-élément ou un pseudo-sélecteur
  • Il ne faut jamais utiliser l’esperluette à l’intérieur des noms de blocs, d’éléments ou de modificateurs

.promo {
    /** Correct : l’esperluette se trouve devant une pseudo-classe **/
    & :hover { …}

    /** Correct : l’esperluette se trouve devant un séparateur d’élément BEM **/
    & __item {
        /** Pas correct : l’esperluette se trouve à l’intérieur du nom de l’élément **/
    	& -link{ …}
    }

    /** Pas correct : l’esperluette se trouve à l’intérieur d’un nom de bloc **/
    & -shover{ …}

    /** Correct : l’esperluette se trouve devant un séparateur de modificateur **/
    & --large{ …}
}

Organisation des fichiers : un bloc BEM = un fichier

Lorsque vous travaillez avec des préprocesseurs CSS, le système de fichiers fonctionne de manière très simple : chaque bloc est décrit dans son propre fichier. Le fichier avec les styles du bloc BEM doit être nommé de la même manière que le bloc.

Ce système concerne également les préprocesseurs HTML (pug, handlbars etc.).

Deux exemples d’organisation de fichiers :

src/

blocks/# dossier avec des blocs BEM

page-header/# dossier de bloc BEM page-header

img/# images que l’on utilise dans le bloc BEM

page-header.less# style de bloc BEM

page-header.pug# html de bloc BEM

page-header.js   # script de bloc BEM

page-footer/      # dossier de bloc BEM

logo/# dossier de bloc BEM

less/global/# dossier avec les styles généraux

global-fonts.less # initialisation des polices de caractère

global.less# styles body, liens, etc.

global-type.less # typographie

mixins/# dossier avec des mixins

style.less# ficher d’import de styles

variables.less# variables de projet

src/

templates/

     blocks/

         page-header.pug# html de bloc BEM

         page-footer.pug# html de bloc BEM

         logo.pug# html de bloc BEM

index.pug# html de la page

less/

    blocks/

         page-header.less# style de bloc BEM

         page-footer.less# style de bloc BEM

         logo.less# style de bloc BEM

img/# images du projet

scripts/# scripts du projet

vendor/# bibliothèques utilisées dans le projet

 

 

Un exemple simple de bas de page fait en utilisant BEM

Pour conclure, je vous propose de voir un exemple simple de bas de page fait en utilisant BEM.


<footer class="page-footer">
    <div class="page-footer__wrapper">
        <section class="page-footer__contacts">
            Address
            <a href="tel:+78125556666"><span class="page-footer__contacts-hidden">Tel.:</span>+9 999 9999
        </section>
        <section class="page-footer__social">
            <div class="social-links">
                <a class="social-link social-link--vk"href="#">Vk</a>
                <a class="social-link social-link--fb"href="#">Fb</a>
                <a class="social-link social-link--inst"href="#">Inst</a>
            </div>
        </section>
        <section class="page-footer__copyright">
            Fait à:
            <a class="page-footer__copyright-link btn"href="#">Copyright </a>
        </section>
    </div>
</footer>

Personnellement, j’utilise cette méthodologie de manière systématique depuis déjà deux ans et je peux dire que cela m’a significativement simplifié la vie. C’est vrai qu’au début, cela peut paraître rébarbatif et inhabituel à cause des noms de classes particulièrement longs et de la nécessité de nommer les classes pour pratiquement toutes les balises. Mais cette possibilité de travailler efficacement et en douceur sur de grands projets évolutifs et d’écrire un code élégant, propre et facilement maintenable vaut la peine de commencer à travailler avec la méthodologie BEM.

Et vous, avez-vous déjà utilisé cette méthode ? Seriez-vous prêt à l’adopter sur vos futurs projets ?

P.S. : J’ai repris dans cet article certains exemples et conseils utilisés par Nikolaï Gromov, un développeur russe, dans son blog. Et je le remercie ici-même pour son autorisation.