Les méthodologies BEM & ABEM en CSS
Introduction
Avoir du code CSS (Cascading Style Sheets) propre et maintenable n’est pas toujours aussi simple qu’on ne le pense. Sur des applications conséquentes, il est très rapide d’arriver à un/des fichier(s) CSS de plusieurs milliers de lignes, et ne plus s’y retrouver au moment d’y faire des modifications ou d’y ajouter de nouveaux éléments. Sans parler du moment où un nouveau développeur arrive en cours de projet, avec ces classes dans tous les sens à devoir comprendre et utiliser…
Trouver des noms de classes étant à la fois explicites, mais ni trop génériques, ni trop précis n’est pas toujours évident.
C’est pour répondre à ces problématiques et simplifier l’écriture du CSS que plusieurs méthodologies ont été créées.
Dans cet article, nous allons nous pencher sur deux de ces conventions que sont BEM & ABEM, que j’illustrerai avec des exemples.
Ps : Pour des raisons de compréhension et de “simplicité”, je garderai les mots anglais des acronymes.
Construction de l’exemple
Pour illustrer ces méthodologies, nous allons partir du même code source CSS, que nous adapterons.
Nous prendrons l’exemple d’une barre de navigation (<nav>
) avec un menu (<ul>
) et quelques items (<li>
). Dans ce menu, chaque item a une couleur différente et le premier item est celui qui actif par défaut (le changement ensuite se fait grâce à du JavaScript, mais cette partie ne nous intéresse pas) :
<nav class="nav-block">
<ul class="menu-block">
<li class="green-menu-item active-item">First item</li>
<li class="blue-menu-item">Second item</li>
<li class="red-menu-item">Third item</li>
</ul>
</nav>
Le style précis et ce que contiennent les classes nous importe peu, nous n’allons donc nous intéresser qu’à la structure HTML, les noms de classes seront assez explicites pour comprendre à quoi ils servent.
Évidemment, nous avons ici un menu tout à fait horrible visuellement, mais il sera parfait pour notre exemple !
Avec du CSS classique, nous aurons donc un problème qui surviendra directement : la redondance de code.
Nos items de menu ayant tous une couleur différente, ils auront tous une classe particulière, alors qu’ils auront la même taille et le même aspect général.
De plus, si l’on a plusieurs menus sur notre page (ce qui arrivera probablement avec un menu dans le header
et un dans le footer
par exemple), on ne saura pas à quel menu il appartient juste en voyant ses classes, il faudra donc éplucher le code HTML pour le savoir.
Remédions à tout cela avec la méthodologie BEM !
BEM (Block Element Modifier) : B__E — M
Principe
La méthodologie BEM repose sur un principe, qu’est la composition d’une page web en différents types d’entités :
Block : c’est un composant générique, ou “parent”, contenant un ou plusieurs elements (notre deuxième type de composants). Celui-ci est une partie “globale” de la page, lorsqu’un block est retiré du contexte de la page, il garde toujours du sens.
Prenons les exemples du site officiel de BEM pour schématiser ce qu’est un block.
Tous ces composants sont bien des blocks puisque, si l’on retire par exemple le “menu block” et qu’on le met dans une autre page : ça reste un “menu block” tout à fait cohérent (nous verrons la différence avec un element).
Element : le second composant est un element, celui-ci est plus spécifique et précis que le block. Un element ne peut pas être utilisé en dehors de son block parent. L’exemple qui suit, provenant également du site officiel, l’illustre parfaitement : un item de menu ne peut être utilisé hors de son menu.
Cette fois, contrairement au block, nous ne pouvons pas retirer un element de son contexte, ou il perd tout son sens… Pour reprendre l’exemple de ce “menu block”, cela signifie que nous aurions un item de menu sans son menu.
Une fois que nous avons ces deux types de composants, nous pouvons nous servir de la dernière terminologie constituant la méthodologie BEM.
Celle-ci est un peu à part puisqu’elle n’est pas un composant à part entière mais elle s’utilise “sur” un composant : le modifier.
Modifier : c’est une entité qui définit l’apparence d’un composant (block ou element).
Continuons avec les exemples du site officiel, grâce au schéma suivant.
Sur cette illustration, on remarque que l’apparence des éléments de menus est différente entre un élément du menu actif, et un autre qui ne l’est pas. Ceci se fait grâce aux modifiers.
Conventions de nommage
Afin de rendre les noms de classes le plus compréhensible possible, des conventions de nommage sont utilisées, ce qui permet d’avoir une syntaxe similaire partout dans le code (CSS, JavaScript & HTML).
Block : au sein même d’un nom de block, c’est le “kebab case” (ou spinal case) qui est utilisé. C’est-à-dire que la séparation des mots se fait grâce à des tirets (dashes), comme ceci :
block-name
Element : comme pour le block, c’est le “kebab case” que l’on utilise dans le nom d’un élément. En revanche, étant donné qu’un élément fait partie d’un bloc, on doit spécifier le nom du bloc parent avant le nom de l’élément. Ce qui donne le résultat suivant :
block-name__element-name
Modifier : Tout comme les blocks et elements, la convention au sein du nom d’un modifier est également le “kebab case”.
block-name--modifier-name
block-name__element-name--modifier-name
La séparation entre chaque composant se fait en “snake case”, mais de façon un peu particulière. En effet, ce n’est pas un mais deux underscores qui sont utilisés. La séparation entre un composant et un modifier se fait quant à elle grâce à deux tirets. Cette différence permet d’un simple coup d’oeil de faire la différence entre un element et un modifier.
Mise en place
Afin de comprendre au mieux l’utilisation et la mise en place de la méthodologie BEM, reprenons le code de l’exemple précédent, et adaptons-le.
Voici donc que ça donnerait :
<nav class="nav-block">
<ul class="menu-block">
<li class="menu-block__item menu-block__item--green menu-block__item--active">First item</li>
<li class="menu-block__item menu-block__item--blue">Second item</li>
<li class="menu-block__item menu-block__item--red">Third item</li>
</ul>
</nav>
Comparé au CSS “classique”, on constate que les noms des classes sont plus explicites et précis, d’un simple coup d’œil, on remarque quel(s) element(s) fait/font partie de quel block, et quel(s) composants ont un aspect différent des autres grâce à leur(s) modifier(s).
C’est bien ici que se situe l’utilité de la méthodologie BEM.
Limites & inconvénients
Cette méthodologie permet d’avoir des classes justement dosées, pas trop spécifiques, ni trop génériques tout en ayant des noms de classes constitués de façon similaire dans toute notre application.
Cependant, elle a des limites. Nous remarquons que dès que l’on a des modifiers à mettre en place, on se retrouve très vite avec un attribut class
long et complexe où il est difficile de s’y retrouver.
Nous verrons avec la méthodologie ABEM comment remédier à ce problème.
ABEM (Atomic Block Element Modifier) : A-B__E -M
Principe
ABEM n’a pas de site officiel mais voici un superbe article dont je me suis inspiré afin d’écrire celui-ci. La méthodologie se définit comme étant “A more useful adaptation of BEM” (Une adaptation plus utile de BEM) et donc comme une “amélioration” de la méthodologie BEM vue précédemment. Cela signifie que son principe est sensiblement le même, il fonctionne également sur le principe de block, element, modifier.
La différence majeure est que l’on y ajoute une notion d’Atomic Design, qui est une méthodologie permettant de séparer nos composants afin d’augmenter la ré-utilisabilité de ceux-ci.
Dans l’Atomic Design, on distingue 5 types de composants : atoms, molecules, organisms, templates et pages. Dans notre cas, nous n’en aurons besoin que de 3 : atoms, molecules, et organisms. Les deux derniers ne sont pas nécessaires, nous n’en aurons pas l’utilité dans notre méthodologie.
Atom : c’est un composant très simple qui n’est généralement qu’un seul élément comme un bouton ou un lien.
Molecule : c’est un petit groupe d’éléments qui fonctionnent ensemble. Par exemple, ça peut être un champ de formulaire avec son label associé.
Organism : cette fois-ci, nous avons affaire à des composants plus importants et complexes, qui regrouperont plusieurs molecules. Pour poursuivre sur l’exemple précédent, le formulaire entier peut être vu comme un organism.
Conventions de nommage
La convention de nommage de la méthodologie ABEM comporte des similitudes avec celle de la méthodologie BEM, avec quelques nuances et ajouts tout de même qui sont les suivants.
Gestion de la casse
Pour commencer, une première différence se fait au niveau de la gestion de la casse au sein d’une section. En effet, ce n’est plus le “kebab case” qui est utilisé mais le “camel case”.
Pour reprendre l’exemple précédent, le nom d’un block donne donc désormais ceci :
blockName
Un avantage de cette nouvelle gestion de la casse peut être lorsque l’on souhaite gérer des elements enfants d’autres elements, ce que l’on appelle des “grandchilds”. En effet, cela permet d’avoir une différence claire entre un “grandchild” et un simple “child”.
Un exemple sera plus parlant, voyons donc la différence entre BEM & ABEM sur les possibilités de nommage des “grandchilds” (aucune convention ne donne LA solution à adopter, ce sera donc à vous de choisir) :
// BEM
block-name__element-name-grandchild
block-name__element-name_grandchild// ABEM
blockName__elementName-grandchild
blockName__elementName_grandchild
On remarque rapidement que peu importe le nommage choisi pour les “grandchilds”, celui-ci est bien plus lisible avec la méthodologie ABEM.
Mon choix personnel se tourne vers la séparation en “kebab case” (premier choix ABEM de l’exemple ci-dessus). La casse au sein d’un nom se faisant désormais en “camel case” et les modifiers étant complètement séparés, le tiret n’est plus utilisé au sein du nom d’un composant, ce qui permet de rester clair. Ceci n’est pas le cas si l’on décide d’utiliser la séparation en “snake case” car l’underscore est déjà utilisé pour faire la séparation entre le block et l’element. Même si ce “snake case” est un peu custom grâce à l’utilisation de deux underscores au lieu d’un seul, cela peut parfois porter à confusion.
Atomic Design
Ensuite, comme mentionné tout à l’heure, une notion d’Atomic Design est présente dans la méthodologie ABEM. Afin de la mettre en place, on se sert de préfixes. Devant chaque nom de classe, nous aurons donc un préfixe a/m/o pour atom/molecule/organism.
À ce moment, on comprend déjà un peu mieux l’utilité du “camel case” plutôt que du “kebab case”. En effet, si l’on intègre ce principe atomique à un composant utilisant la méthodologie BEM, et son équivalent en méthodologie ABEM, on voit nettement que le second est plus lisible que le premier :
// BEM
.m-subscribe-form__field-item {}// ABEM
.m-subscribeForm__fieldItem {}
Dans l’exemple ci-dessus, la séparation des mots via un tiret rend le tout ambigu, on ne sait pas si le m-
fait partie du nom du block ou s’il représente l’atomicité. En revanche, grâce à l’utilisation du “camel case”, on distingue très bien la différence et on sait directement ce à quoi il correspond.
Pour vous convaincre pleinement de la différence que cela apporte, je vais vous montrer un exemple également présent dans l’article dont j’ai mis le lien plus haut, il est très parlant.
Pour bien se rendre compte, il suffit de ne garder chaque silhouette, puis se demander ensuite, quelle partie de celle-ci correspond à quelle partie du nom de la classe (block, element, …). Essayez de trouver quelle partie de la silhouette correspond à quelle partie :
Dans la première silhouette, quelques interrogations peuvent se poser. Alors que dans la seconde, grâce au “camel case” aucun doute n’est possible, on comprend parfaitement chaque séparation (tiret comme underscore). Ces deux silhouettes représentent pourtant exactement le même élément, comme le montre le résultat ci-dessous :
Gestion des modifiers
Un des désavantages évidents de la méthodologie BEM est la complexité que peut vite prendre un certain composant lorsque des modifiers entrent en jeu.
Reprenons l’exemple utilisé dans la partie “Limites et inconvénients” de la section BEM. Nous avions ceci :
<div class=”block-name__element-name block-name__element-name--size block-name__element-name--color”>
<!-- Content -->
</div>
Avec la méthodologie ABEM, nous aurons beaucoup moins de difficultés
<div class=”a-blockName__elementName -size -color”>
<!-- Content -->
</div>
Le fait de séparer les modifiers permet d’éviter de réécrire tous les noms des composants sur lesquels ils se situent, et encore mieux : ça les rend ré-utilisables !
Similitudes
Mise à part ces deux différences majeures, les conventions de nommage restent similaires avec la méthodologie BEM. Autrement dit, on conserve les doubles-undescore dans la séparation de nos entités block/element et on conserve ces trois notions de block, element et modifier.
Mise en place
Reprenons l’exemple de notre article et adaptons-le cette fois à la méthodologie ABEM, et voyons ce que ça donne :
<nav class="o-navBlock">
<ul class="m-menuBlock">
<li class="a-menuBlock__item -green -active">First item</li>
<li class="a-menuBlock__item -blue">Second item</li>
<li class="a-menuBlock__item -red">Third item</li>
</ul>
</nav>
C’est parfait !
Nous n’avons plus les inconvénients de notre CSS classique avec la redondance de code et la non-spécificité de nos composants, et nous perdons également l’inconvénient principal de la méthode BEM et ses attributs class interminables.
Le tout restant compréhensible, clair et lisible.
Ce que les créateurs de ABEM reprochent à la méthodologie BEM est principalement la spécificité un peu “too much” qu’apportent les modifiers, du moins dans la façon où c’est mis en place.
Grâce à cette nouvelle approche, ceci n’est plus un problème, et c’est tout aussi facile à mettre en place. En effet, les modifiers peuvent être tout aussi spécifiques, comme ils peuvent ne pas l’être en cas de besoin, il suffit d’adapter son CSS à ce que l’on souhaite, pour reprendre l’exemple de notre menu, nous avons plusieurs choix de mise en place CSS pour les couleurs :
/* Pour une implémentation spécifique */
.a-menuBlock__item.-green {
background-color: #00C81b;
}
/* Pour une implémentation non spécifique */
.-green {
background-color: #00C81b;
}
Conclusion
Nous avons vu dans cet article les deux méthodologies CSS que sont BEM & ABEM (qui est une “amélioration” de BEM).
Cependant, il faut savoir que d’autres méthodologies existent, comme OOCSS (Object-Oriented CSS), SUIT CSS, ou encore SMACSS (Scalable and Modular Architecture for CSS), entre autres…
J’ai préféré me pencher sur BEM & ABEM étant donné que ce sont les deux méthodologies que je privilégie (surtout ABEM, évidemment) lors de mes développements que ce soit dans mes projets personnels ou professionnels.
Il existe également des tutoriels sur ces autres méthodologies, à vous de vous forger votre propre opinion, vous connaissez la mienne ! 😉