BEM en équipe agile

Conseils et retour d’expérience

J’aime coder du CSS mais sous une seule condition: quand il est optimisé. Car s’il y a bien un langage où on peut créer un joyeux bazar c’est bien celui là.

CSS est facile à apprendre et malheureusement très permissif. Travailler sur un site web dont le CSS déjà produit n’est pas de qualité multiplie les temps de maintenance de celui-ci, même si l’évolution à apporter est minime (l’effet iceberg voire pire, domino). Pourtant, il n’est pas si compliqué de combler les lacunes historiques de ce langage, cela demande juste un peu de rigueur.

Parmi de nombreuses techniques que l’on trouve pour optimiser la production du CSS, existe la méthode BEM. C’est une méthode de nommage que j’ai pu apprivoiser à la fois à travers de petits projets de sites vitrines mais aussi pour la refonte d’une grosse application web.

Les principes de BEM

La méthode BEM repose sur deux pratiques d’écriture CSS assez populaires:

  • l’utilisation exclusive de classes pour cibler les composants de l’interface auxquels appliquer les styles
  • la vision orientée objet: inspirée de la Programmation Orientée Objet (POO), on pense l’interface en composants autonomes, on parle alors de CSS Orienté Objet (CSSO).

Sur les même principes, vous connaissez peut-être le fameux objet media de Nicole Sullivan qui sauve paraît-il des centaines de ligne de code.

BEM est en fait une base pour vos conventions d’écriture CSS. La méthode consiste à diviser les composants d’une interface en 3 types:

  • les Blocs ou Blocks: les composants autonomes, que l’on peut retrouver à plusieurs endroits d’une page ou du site/application. Exemple: un menu, un formulaire de login, un fil d’ariane.
  • les Elements: ce sont des composants non autonomes, car toujours rattachés à un bloc parent. Exemple: un lien de menu, un champ dans un formulaire de login, un lien dans un fil d’ariane.
  • les Modificateurs ou Modifiers: c’est ce que j’appelle les variantes. Les modificateurs sont toutes les classes qui vont nous permettre de rajouter quelques propriétés CSS aux blocs ou éléments pour en créer des versions alternatives. Ex: un lien actif dans un menu, un champ erroné dans un formulaire de login …

De là découlent 3 façons d’écrire les sélecteurs classes qui vont cibler ces composants. Bien qu’il existent plusieurs façons d’écrire du BEM — puisque l’essentiel de la méthode est le découpage Block-Element-Modifier — celle que j’utilise est la version officielle:

  • les classes de blocs s’écrivent .nom-du-bloc ( les tirets sont tolérés pour les noms composés)
  • les classes d’éléments s’écrivent .nom-du-bloc__nom-element. Parce qu’un élément appartient à un bloc, on doit toujours préfixer le nom de l’élément par le nom de son bloc, puis par deux underscores.
  • les classes de modificateurs des blocs s’écrivent .nom-du-bloc — modifier et les modificateurs d’éléments s’écrivent .nom-du-bloc__nom-element — modifier
Exemple de découpage BEM d’un formulaire. Le bloc formulaire sera ciblé en CSS par la classe .form, un élément label par .form__label, un élément champ de saisie .form__field. On considère les boutons et les cases à cocher comme des blocs autonomes car on suppose qu’ils peuvent se retrouver en dehors du bloc formulaire. On les sélectionnera respectivement par les classes .button et .checkbox. Pour l’état « coché » de la checkbox, on lui rajoutera en plus de sa classe de base, une classe modificatrice .checkbox–checked.

C’est un peu verbeux je vous l’accorde, mais avec un peu d’habitude on s’y fait très bien. BEM permet de se poser moins de questions lors du nommage des sélecteurs et permet dans le CSS, de repérer rapidement à quel élément se réfère une classe.

Si vous voulez plus de détails sur la méthode BEM, le site suivant est très bien fait: http://getbem.com/ (en anglais).

Mettre en place BEM en équipe agile

Le but de cet article n’est pas de s’étendre sur la méthode théorique de BEM, mais bien de parler de sa mise en pratique en équipe.

Comme je l’ai évoqué plus haut, j’ai donc pu confronter BEM au contexte réel dans le cadre d’une refonte d’une application logicielle complexe en version Angular. Dans notre équipe agile d’une dizaine de développeurs, j’avais la responsabilité d’organiser l’environnement d’intégration.

Compte tenu de la richesse des fonctionnalités et du caractère très modulaire de l’application, j’ai très vite proposé la solution BEM comme base de notre convention CSS. Cela permettait de poser une organisation précise du CSS dès le début, peut-être discutable dans le cadre de petits projets, mais incontournable pour les gros. Malgré tout, il ne faut pas voir en BEM le sauveur de vos projets d’intégration complexes, car pour que cela marche, il faut faire attention à plusieurs choses.

Ne pas sous-estimer la complexité de BEM

Le plus important est de bien comprendre et bien appliquer les 3 types block, element et modifier. En théorie, c’est très simple, mais le plus compliqué est de s’y tenir, surtout quand on est plusieurs, avec des niveaux de compétence différents en CSS. En contexte agile, tout le monde peut et doit participer à tout d’où le devoir de chacun de transmettre ce qu’il sait aux autres.

En tant qu’intégrateur acceptez les lacunes des autres dans votre domaine et placez vous plutôt dans une optique de conseil et d’information constante. Si besoin, n’hésitez pas à être quand même ferme sur le besoin de respect de la convention et des bonnes pratiques d’intégration. C’est votre rôle en tant qu’expert CSS dans l’équipe agile (tout comme l’expert Angular pourra vous taper sur les doigts si vous mettez tout votre code JS dans le controller). Tout le monde ne comprend pas forcément le besoin de rigueur dans un langage aussi permissif que le CSS.

Accepter l’inévitable dette technique, mais la minimiser

Si vous aimez être rigoureux dans votre travail, lâchez donc du lest dans votre perfectionnisme. Un peu de dette technique s’installera forcément. D’abord car plusieurs personnes vont coder du CSS, mais aussi car le contexte agile fait qu’on ne connaît pas dès le début tous les composants et variantes de l’interface finale.

Attendez vous donc à faire du refactoring. Cela ne sera d’ailleurs pas un mal puisque vous vous entraînerez à reconnaître les erreurs fréquentes (les vôtres et celles des autres) et vous écrirez ensuite des classes plus judicieuses, plus maintenables.

Pour minimiser la dette technique, essayez tout de même de faire preuve d’anticipation. Par exemple, même si un composant ne se répète pas encore dans l’application mais qu’il y a de fortes chances que cela arrive, choisissez des noms de classes assez objectifs qui permettront au composant d’être réutilisable ensuite dans toute l’application ou le site.

Vérifier constamment le CSS avec CSSlint ou Sasslint

L’une des choses les plus importantes dans la bonne pratique de BEM, c’est de savoir s’entourer des bons outils. Vérifiez régulièrement la qualité du code CSS de votre application, idéalement de manière automatisée via un task runner comme Grunt ou Gulp. Si vous utilisez Sass comme préprocesseur avec Gulp, le plugin gulp-scsslint fait ça très bien. Vous aurez alors un fichier de configuration en .yml qui vous permettra d’ajuster les vérifications selon votre convention d’écriture et selon les principes BEM. Quand les classes ne seront pas écrites correctement dans les fichiers CSS, votre tâche Gulp ou Grunt renverra une erreur dans la console.

Mettre en place un styleguide

Un styleguide est une sorte de documentation très visuelle qui recense tous les composants d’une interface. On y renseigne leur aspect visuel et les classes qui leur sont appliquées:

Extrait du styleguide du site de Fontshop

C’est très pratique, et on voit de plus en plus d’entreprises les partager en ligne (voir celui de Fontshop). Avec les méthodes Agile qui se développent et l’industrialisation du web, on tend de en plus vers le styleguide-driven development, c’est à dire le développement à partir d’un styleguide, qui sert alors de banque d’éléments où piocher et non plus à partir d’une maquette statique.

Le styleguide est là comme une référence à la fois pour l’équipe de développement mais aussi pour l’équipe de design. L’intérêt est que cette dernière puisse concevoir les éléments d’interface en toute logique, selon ce qui a déja été développé dans les précédents sprints. Ce qui évitera par exemple de coder 42 styles de boutons dans l’ajout des briques suivantes. Vous pouvez également y renseigner les typos, les icônes ou les codes hexadécimaux des couleurs de l’interface.

Le plus difficile, bien sûr, est d’habituer toute l’équipe à son usage. N’hésitez pas à vous y référer régulièrement, informez les autres de vos ajouts par mail ou messagerie, et incorporez carrément le styleguide comme une partie du site ou de l’application développée. Cela peut également devenir un livrable à montrer au client, ce sera alors un gage de sérieux.

L’idéal est que les ajouts dans les styleguides soient automatisés, par le biais de commentaires de documentation directement inclus dans les fichiers CSS. J’ai essayé plusieurs solutions techniques pour le projet, mais aucune n’interprétait Angular (sauf le module SC5 que je n’ai pas réussi à faire fonctionner correctement). Ce qui fut dommage car plusieurs de nos blocs définis en CSS étaient également des directives, c’est-à-dire des composants dynamiques dépendant d’Angular pour leur bon fonctionnement. Nous nous sommes contentés d’un styleguide manuel, qu’il a fallu mettre à jour avant chaque fin de sprint (et avec le rush de fin de sprint ça passe parfois à la trappe).

Pour tout savoir sur les styleguides et voir d’autres exemples, n’hésitez pas à faire un tour sur styleguides.io (ressource en anglais).

Quelques problèmes rencontrés avec BEM

En adaptant BEM à notre contexte réel, on s’est retrouvé parfois avec quelques interrogations sur la méthode. Eh oui, car dans la jolie documentation officielle, on ne vous informe pas forcément de tous les cas problématiques qui peuvent se poser.

Les éléments dans des élements

Le problème qui s’est imposé le premier a été celui du nommage d’éléments imbriqués dans d’autres éléments. Nous étions tentés d’écrire:

.bloc__element__element {}

Mais de cette manière, on recréait la hiérarchie du DOM dans le CSS, ce que BEM essaie justement d’éviter.

En fait, la bonne pratique est de considérer tous les éléments d’un bloc sur un même niveau. Tous sont des enfants du bloc, il n’y a aucun sous-enfant possible. Ainsi, dans un bloc formulaire nommé .form, on nommera un groupe de champ .form__fieldset et un champ de saisie .form__field , et non .form__fieldset__field.

Les blocs dans des blocs

Après les éléments dans les éléments, on peut également se retrouver avec des blocs dans des blocs.

Le cas typique est celui d’un bouton .button dans un formulaire .form.

Pour cibler tous les boutons de formulaire, il faut utiliser le sélecteur .form .button et non .form__button à proscrire absolument. Sinon vous allez vous retrouver avec 150 classes pour le même style, ce qui n’est pas le but.

L’application des modificateurs

Avec BEM, on est souvent tenté de créer beaucoup de classes.Par exemple, pour une variante d’un bloc, comme un état actif, on aurait tendance à rajouter une classe modifier à tous les éléments dans ce bloc qui sont modifiés visuellement par l’état actif de ce parent.

Mais quand applique une classe modifier pour un élément, il faut toujours se demander si son élément frère subit aussi une modification au même moment. Si la réponse est oui, c’est que la classe modifier s’applique très certainement au bloc parent et non à ses éléments. Par exemple, pour une navigation horizontale, l’erreur serait d’appliquer les classes suivantes:

<nav class="nav">
<ul class="nav__items-list nav__items-list--inline">
<li class="nav__item nav__item--inline"></li>
<li class="nav__item nav__item--inline"></li>
</ul>
</nav>

Parce que la variante « inline » modifie à la fois l’élément .nav__items-list et les éléments .nav__item, il est plus judicieux de l’appliquer seulement au bloc parent , même si les propriétés modificatrices ne s’appliquent pas à lui mais à ses enfants.

<nav class="nav nav--inline">
<ul class="nav__items-list">
<li class="nav__item"></li>
<li class="nav__item"></li>
</ul>
</nav>

Dans le fichier CSS, on ciblera alors les éléments modifiés de cette manière:

.nav--inline .nav__list {} .nav--inline .nav__item {}

Ainsi, le sélecteur modificateur .nav — inline .nav__item composé de 2 classes, aura plus de poids que le sélecteur de base .nav__item et le surchargera de toute manière pour en modifier les propriétés, qu’importe sa place dans le fichier CSS.

L’intérêt dans BEM est quand même de créer le moins de classes possibles pour ne pas complexifier inutilement la maintenance de votre code.

Appliquer BEM partout ?

Appliquer à la lettre cette méthode dans tout notre CSS n’est pas une bonne idée. Parfois, il arrive d’avoir besoin de certaines classes utilitaires. Par exemple, on peut avoir une classe .t-error (t comme diminutif de texte) qu’on applique à n’importe quel élément texte pour appliquer une couleur rouge qui symbolise une erreur. Ce genre de classes abstraites n’est pas faite pour être « BEMisée ». Il faut utiliser le nommage BEM seulement pour les portions d’HTML ou CSS qui représentent des composants ou des éléments de composant.

Faire du bon BEM ça prend du temps

En conclusion, malgré des principes très simples, il n’est pas toujours facile d’appliquer la méthode BEM. Comme toute convention d’écriture, cela demande un temps nécessaire d’apprentissage et d’adaptation. Avec du recul, je pense toujours que c’est une très bonne approche pour les intégrations complexes et modulaires. Qui plus est, la méthode s’adapte bien aux concepts d’Angular et m’a tout l’air d’être parfaite pour les composants React. Si vous ne l’avez pas encore testée, faites vous la main sur de petits projets, elle reste efficace pour les intégrations rapides, et vous pouvez même vous constituer votre propre banque de composants BEM réutilisables !

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.