Introduction▲
Des années durant, j'ai intégré des sites Web et développé des applications JavaScript sans ressentir le besoin d'une méthodologie pour nommer les classes CSS. Puis, les projets grossissant, le code CSS est devenu douloureux…
L'épineux sujet du nommage en CSS est loin d'être fermé. Depuis le début de la décennie, plusieurs auteurs majeurs ont partagé leurs recherches. Ils ont apporté un regard nouveau et sont allés à contrecourant, en rupture avec ce qui faisait jusqu'alors consensus. Je raconte dans cet article mon propre cheminement dans leurs travaux en espérant qu'il sera utile à l'intégrateur Web autant qu'au développeur JavaScript. J'ai cherché en effet une approche adaptée à la fois aux pages et aux applications Web.
I. OOCSS▲
Je me suis d'abord tourné vers OOCSS (Object Oriented CSS), un ensemble de bonnes pratiques initiées par Nicole Sullivan (Stubbornella).
Le concept de OOCSS est de repérer des « objets CSS », c'est-à-dire des « patterns visuels » qui se répètent, et de définir ainsi des classes réutilisables. La méthode consiste à prendre le design comme point de départ : on repère des répétitions visuelles puis on les nomme. La sémantique du document n'est donc plus une base de travail, et des classes CSS nommées selon l'apparence sont autorisées à partir du moment où elles sont génériques. En cela, OOCSS est en décalage avec les bonnes pratiques des années 2000. L'ancien nommage par la sémantique préconisait des classes comme .last-articles-box ou .comment-title, alors qu'en OOCSS des classes .links-box ou .tiny-title seront préférées. Cela implique que le pilotage de l'apparence se fait depuis le code HTML. Ainsi, lorsqu'une feuille de styles écrite à la manière OOCSS est bien faite, il devient possible d'ajouter des morceaux entiers dans le design sans toucher à la feuille CSS, juste en réutilisant des objets CSS déjà existants.
OOCSS met en avant deux principes :
- Le principe de séparation de la structure et de l'apparence ;
- Le principe de séparation du conteneur et du contenu.
Le premier principe nous fera préférer, dans les sélecteurs CSS, les classes plutôt que des identifiants ou des noms d'éléments HTML. Il incite également à factoriser les propriétés visuelles répétées. Pour l'exemple, partons du code HTML suivant :
Mieux vaut factoriser dans une classe btn les règles CSS communes aux deux boutons, ce qui donne :
Le second principe consiste à éviter des cascades CSS comme .links-box .title, car l'apparence du contenu .title serait alors couplée au conteneur .links-box. Une classe .box-title sera plus réutilisable.
Je recommande au lecteur l'exposé de Nicole Sullivan : Our best practices are killing us, publié en 2011. Cet exposé est formidable. Il a été, pour de nombreux intégrateurs, le point de départ d'une aventure OOCSS. Le lecteur désirant aller plus loin lira ensuite cette introduction à OOCSS (en français) ainsi que le wiki du framework OOCSS.
Mais OOCSS correspondait imparfaitement à mon besoin. Les designers avec lesquels je travaille conçoivent trop de variations entre chaque bloc. OOCSS ne donne pas des règles de construction nettes et fermes, et le temps passé à factoriser l'apparence peut dépasser le gain de la réutilisation. L'approche OOCSS aide certainement à afficher des milliers d'objets dans une plateforme tentaculaire comme Facebook, car le designer lui-même raisonne alors par objets. Elle reste globalement inappropriée pour intégrer un joli design monolithique fait sur mesure pour un site plus modeste. En outre, dans le cadre de la conception d'une application JavaScript, le découpage de l'interface ne devrait pas être fait par l'apparence, les ressemblances entre composants différents étant toujours superficielles et amenées à diverger.
Quoi qu'il en soit, les nouvelles bonnes pratiques de OOCSS font prendre conscience que l'ancien nommage par la seule sémantique est obsolète dans la mesure où il nous fait produire du code CSS non-réutilisable. Mettons OOCSS de côté pour le moment, nous y reviendrons plus tard.
II. BEM▲
La méthodologie BEM est une solution élaborée par Yandex et publiée en 2010. BEM a deux faces : il s'agit d'abord d'une méthode déclarative de l'interface utilisateur servant à décrire un « arbre BEM », les outils open source de Yandex travaillent ensuite sur cet arbre. BEM apporte également une convention de nommage des classes CSS qui a gagné une certaine popularité. C'est de cette méthodologie du nommage, véritablement puissante, que nous allons parler ici.
BEM est l'acronyme de Block, Element, Modifier, et toute la méthodologie du nommage à la manière BEM tient dans ces trois mots. La force du concept ? Ce qui compose un page ou une application Web peut toujours être rangé dans une arborescence de blocs, d'éléments et de modificateurs.
Un bloc est une entité indépendante, une « brique » de l'application ou de la page Web. Un bloc forme son propre contexte autonome. Ci-dessous des exemples de blocs dans une illustration tirée du site officiel :
Un élément est une partie d'un bloc. Le contexte d'un élément est celui du bloc. Ci-dessous deux exemples empruntés toujours au site officiel :
En tant que « brique de construction », un bloc est réutilisable dans d'autres blocs ou dans des éléments. Il ne connait toutefois que son propre contexte et non celui du parent. Un bloc n'est donc pas livré avec les règles CSS de son propre positionnement au sein du conteneur parent. Nous éclaircirons ultérieurement, sur un cas d'utilisation, ce point important.
Un modificateur est une propriété qui sert à créer des variantes, pour faire des modifications minimes comme changer des couleurs. Il existe des modificateurs de blocs et des modificateurs d'éléments.
La méthodologie BEM établit ensuite trois règles essentielles :
- Les blocs et les éléments doivent chacun avoir un nom unique, lequel sera utilisé comme classe CSS ;
- Les sélecteurs CSS ne doivent pas utiliser les noms des éléments HTML (pas de .menu td) ;
- Les cascades dans les sélecteurs CSS devraient être évitées.
À propos de la première règle, précisons que les identifiants HTML (les attributs id) ne doivent pas être utilisés en CSS, chaque bloc pouvant par principe être instancié plusieurs fois. Les identifiants HTML ne servent que d'ancres. La deuxième règle est nécessaire dans la mesure où les blocs peuvent être imbriqués. Un sélecteur .menu td briserait la séparation des contextes en interagissant avec les balises <td> des sous-blocs, cela doit être évité.
Ces règles impliquent de préfixer les noms des éléments par leur contexte. Venons-en à la convention de nommage des classes CSS. Le site officiel prend soin de préciser que seuls comptent les concepts, la syntaxe restant libre. L'équipe de BEM utilise pour sa part une syntaxe à base de underscores :
- block-name
- block-name_modifier_name
- block-name__element-name
- block-name__element-name_modifier_name
Pouah ! Hideux ! La raison d'une telle laideur est un manque de caractères utilisables dans les identifiants en CSS.
Le code CSS, en méthodologie BEM, est presque plat. Voici un exemple de code CSS pour un bloc search-box doté d'un modificateur light, contenant un élément btn avec un modificateur max_visible :
.search-box
{
height:
300
px;
width:
300
px;
}
.search-box_light
{
background-color:
#DEF
;
color:
#777
;
}
.search-box__btn
{
padding:
4
px;
}
.search-box__btn_max_visible
{
font-weight:
bold
;
}
Le code HTML :
<div class
=
"search-box search-box_light"
>
<!-- (input field here) -->
<button class
=
"search-box__btn search-box__btn_max_visible"
>
Search</button>
</div>
Une cascade est utilisée lorsqu'un modificateur de bloc a un effet sur un élément :
.search-box_light
.search-box__btn
{
background-color:
#9AB
;
}
Signalons entre parenthèses que cette cascade est à éviter sur les blocs pouvant s'imbriquer récursivement, car le modificateur du bloc parent affecterait alors les blocs enfants. Fort heureusement le cas est rare.
Nous n'en avons pas terminé avec BEM. Dans la suite de l'article cependant nous nous écarterons de la syntaxe et même de la terminologie originale. Je suggère au lecteur intéressé par l'orthodoxie : la présentation officielle de la méthodologie BEM dont sont tirées les deux illustrations, et un article sur l'utilisation de BEM dans de petits projets (incluant la partie déclarative de l'arbre BEM).
III. Pertinence de BEM▲
BEM, c'est un peu le chic type au visage ingrat. Il a des qualités mais qu'est-ce qu'il est… LAID ! Je ne sais pas pour vous ? En ce qui me concerne, travailler sur un code qui me dégoute, ça, jamais !
Tout de même, juger sur l'apparence n'est pas bien. Donnons-lui une chance et regardons au moins ses avantages.
III-a. La propreté▲
En BEM, aucun risque d'aboutir à ce code-là :
III-b. La performance▲
La performance concerne plus particulièrement les applications Web. Les navigateurs rangent les classes CSS dans une table de hachage globale au document, mais il serait trop couteux de créer des sous-tables pour les descendants au niveau de chaque élément HTML. Aussi, en CSS, seul le premier niveau de sélection est performant. Les cascades CSS, lorsqu'elles sont nombreuses, engendrent des problèmes de fluidité surtout sur les pages animées des applications Web.
BEM, en limitant drastiquement l'usage des cascades CSS, incite à élaborer des feuilles de styles performantes.
III-c. La scalability et une architecture par composants▲
Un bloc peut être placé n'importe où dans la page, ou encore apparaitre (être instancié) plusieurs fois. Cela est possible parce que ses règles CSS sont radicalement séparées de celles des autres blocs. Il est alors possible de construire des applications géantes tout en travaillant toujours à une échelle réduite : le contexte d'un bloc.
Un parallèle est à faire avec les « composants Web », lesquels seront les briques des futures applications JavaScript. Chaque composant Web embarquera ses propres règles CSS et son propre code JavaScript. La norme prévoit un shadow DOM, c'est-à-dire une sandbox qui encapsule une portion de DOM dans le but d'empêcher les sélecteurs CSS et les identifiants HTML d'interagir par erreur avec le reste du DOM. Aujourd'hui, tout le challenge des frameworks JavaScript est de s'adapter à cette manière de travailler.
Les blocs BEM correspondent bien à la philosophie du développement par composants. BEM donne pour les technologies d'aujourd'hui un format de nommage utilisable et une manière de travailler compatible avec celle de demain.
IV. Une syntaxe BEM… jolie !▲
D'un côté BEM en vaut la peine, de l'autre il n'est pas séduisant. Nous risquons le mariage de raison… Les lignes suivantes relatent une démarche qui m'a pris plusieurs mois.
HTML 5 est venu avec une bonne et une mauvaise nouvelle. La bonne nouvelle, c'est qu'il est désormais possible d'utiliser n'importe quel caractère dans les identifieurs des attributs class et id. Et la mauvaise nouvelle, c'est que… pas en CSS. Un auteur Belge a dressé la liste des caractères qui ont un sens spécial en CSS et qu'il faut donc échapper : !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, `, {, |, } et ~. Échapper n'est pas une option, on ne va pas remplacer une laideur par une autre.
Il reste alors deux caractères de séparation : le trait d'union (-) qu'une règle spéciale permet d'utiliser dans un identifieur sauf en première position, et le underscore (_).
Aï.
Bloqué.
Prenons le problème autrement. La norme en CSS dit ceci :
All CSS syntax is case-insensitive within the ASCII range […], except for parts that are not under the control of CSS. For example, the case-sensitivity of values of the HTML attributes "id" and "class" […] (Source : W3C).
La casse est donc utilisable. À titre personnel je rechignais un peu. J'ai toujours nommé mes classes CSS en minuscules avec des traits d'union, tous les gens bien font comme cela. Mais ici il faut sacrifier quelque chose. Accepter un séparateur illisible et moche ? Plutôt mourir ! S'assoir sur la norme ? Bon j'avoue avoir envisagé un temps une utilisation irrégulière du tilde (~)… Mais quoi ! La casse est valide, on l'utilise ailleurs en programmation, zut alors ! À cœur vaillant rien d'impossible, tant pis pour la tradition.
Une syntaxe BEM basée sur la casse est décrite par Nicolas Gallagher dans son mémorable article : About HTML semantics and front-end architecture, 2012. L'auteur en fait usage dans son framework SUIT CSS. La voici :
- ComponentName
- ComponentName--modifierName
- ComponentName-descendantName
- ComponentName.is-stateOfComponent
On l'aura compris, les composants sont les blocs et les descendants sont les éléments. La terminologie de BEM est ambigüe car « bloc » en CSS est aussi un type de flux d'affichage (l'opposé de « en ligne ») et « élément » désigne une balise HTML et son contenu. Aussi, adoptons définitivement les termes « composant » et « descendant ». Un composant CSS évoque en effet avec justesse l'idée d'encapsulation du composant Web, et un descendant en est effectivement un dans l'arbre DOM. Un composant Web pouvant embarquer plusieurs composants CSS, je préfère accoler ainsi « CSS » afin de les distinguer.
La syntaxe de l'état est intéressante : un simple point de séparation. C'est la syntaxe du sélecteur pour deux classes affectées à un même élément HTML. Ce sélecteur est devenu utilisable depuis que Windows XP et donc Internet Explorer 6 ont été abandonnés. Ci-dessous un exemple de bloc MenuBtn marqué avec l'état current en syntaxe SUIT CSS :
<button class
=
"MenuBtn is-current"
>
Open</button>
En revanche, la syntaxe des modificateurs oblige encore, à l'instar de celle originale de BEM, à de lourdes répétitions. Car la classe CSS d'un modificateur est déclarée en plus de celle du composant CSS. Voici comment s'écrira un bouton doté des modificateurs big et darkBlue :
<button class
=
"MenuBtn MenuBtn--big MenuBtn--darkBlue"
>
Open</button>
Pourquoi le principe de la double classe n'a-t-il pas été retenu pour les modificateurs ? L'auteur m'a répondu : « It helps keep specificity low » (« Ça aide à garder basse la spécificité »). Et c'est une réalité. La spécificité CSS est une mesure de la priorité d'un sélecteur CSS. Par exemple, le code suivant affichera en bleu les éléments ayant les deux classes c1 et c2, car la spécificité de .c1.c2 est plus élevée que celle de .c1 :
Toutefois, si c1 était le nom du composant CSS et c2 celui d'un modificateur, alors une spécificité plus haute pour le modificateur aurait du sens. Le but d'un modificateur n'est-il pas précisément de surcharger les règles d'affichage de base du composant ?
En outre, sur le plan de la performance, sélectionner simultanément deux classes CSS revient à faire l'intersection des résultats de deux sélections simples. La complexité, au sens algorithmique du terme, reste dans le même ordre de grandeur. Par conséquent la sélection simultanée de deux classes CSS est performante.
Voici alors la convention de nommage que je propose à mon tour, dérivée de celle de SUIT CSS, dérivée de BEM :
- ComponentName
- ComponentName.modifierName
- ComponentName-descendantName
- ComponentName-descendantName.modifierName
- ComponentName.isStateOfComponent
J'ai gardé l'idée d'une convention pour un état booléen. Mais, visuellement, un état n'est rien d'autre qu'un modificateur spécial et je l'ai donc intégré dans la syntaxe des modificateurs. Voici un exemple de code HTML contenant de surcroit un descendant keyword :
<button class
=
"MenuBtn big darkBlue isCurrent"
>
Open the <b class
=
"MenuBtn-keyword"
>
archives</b>
</button>
Il arrive qu'une classe ne s'applique que sur certains types d'écrans. Un simple suffixe en majuscules fera ressortir cette caractéristique. Par exemple le modificateur stickyMT s'appliquera sur les écrans des mobiles et des tablettes seulement. Je compose pour ma part les suffixes avec D (desktop), T (tablet) et M (mobile). En cas d'écrans multiples, ordonner alphabétiquement.
V. Ingérences transversales et… OOCSS▲
La méthodologie BEM apporte une solide séparation entre les contextes CSS des différents composants CSS. Pourtant, tous ces contextes reposent en définitive sur du sable mouvant. Les composants supposent en effet toujours un certain contexte CSS global, comme l'existence d'un reset CSS pour annuler les marges ou encore le fait qu'une balise <strong> affiche par défaut son contenu en gras.
Et il est à mon avis justifié de jouer volontairement sur la corde transversale en marquant des éléments du DOM avec des classes ne respectant pas l'arborescence des composants CSS. Ainsi, un site réutilisant des patterns visuels globaux à la façon OOCSS fera dépendre de classes CSS transversales des propriétés décoratives. Dans une application JavaScript, un service du support multilingue peut travailler de manière transversale sur des libellés disposant de traductions.
Pour ces marqueurs transversaux, je propose d'utiliser un préfixe commençant par une lettre minuscule et se terminant par le premier trait d'union : myPrefix-…. Par exemple un objet CSS aux propriétés décoratives : ob-prettyBox ; un libellé dont le contenu est modifiable par un service multilingue transversal : lang-localizableLabel. Pas de convention pour ce qui suit le préfixe : lang-LocalizableLabel ou encore lang-localizable-label au bon vouloir du concepteur.
Nota. — Les ingérences transversales sortent de l'orthodoxie BEM.
V-a. À propos d'objets CSS▲
Parmi les objets CSS valides en OOCSS, distinguons ceux purement décoratifs de ceux qui organisent l'interface utilisateur.
Les objets CSS décoratifs qui se limitent aux propriétés sans effet sur le flux d'affichage, comme les couleurs, les ombres, les coins arrondis, sont à consommer sans modération. Cependant, la mise en commun de propriétés décoratives peut aller plus loin. S'il apparait que la moitié des composants CSS partagent un padding de dix pixels et une bordure de deux pixels, pourquoi ne pas définir une classe transversale ob-commonBox ? Grâce aux objets CSS, nous ne sommes plus limités à un seul contexte CSS global, nous pouvons créer de multiples contextes CSS globaux. Ils seront ensuite affectés aux composants ou descendants, et joueront le rôle d'habillage par défaut.
Quant aux objets CSS qui structurent l'interface utilisateur, à l'instar du media object, leur usage coïncide avec celui des composants CSS. Une syntaxe BEM devrait alors être préférée. Ensuite, lorsqu'il s'agit de délimiter nos composants CSS à partir d'un design, l'approche OOCSS consistant à repérer des répétitions de patterns visuels est excellente.
VI. Cas d'utilisation, partie 1 : HTML▲
Un peu de pratique pour fixer les idées. Nous allons élaborer un modèle de page innovant pour un blog. Admirez la beauté :
Quatre grands composants CSS se distinguent aisément : SiteHeader pour l'en-tête, MainContent l'article principal en blanc, Sidebar la barre latérale et SiteFooter le pied de page.
Nous avons également besoin d'un composant PageWrapper pour centrer la page et lui fixer une largeur, et BodyLayout le conteneur de l'article principal et de la barre latérale. Intéressons-nous au code HTML de ce dernier :
<div class
=
"BodyLayout"
>
<main class
=
"BodyLayout-mainContent"
>
<article class
=
"MainContent"
>
<!-- Content here… -->
</article>
</main>
<div class
=
"BodyLayout-sidebar Sidebar"
>
<!-- Widgets here… -->
</div>
</div>
En tant que composants CSS, MainContent et Sidebar ne doivent pas contenir leur propre positionnement. Aussi seront-ils positionnés par des conteneurs mainContent et sidebar (notez bien les premières lettres minuscules qui distinguent les descendants des composants). Et dans le cas de la barre latérale, le descendant sidebar et le composant Sidebar sont associés au même élément HTML. Cela est autorisé par BEM : on dit alors du nœud du DOM qu'il est un mix des deux.
Les mix sont pratiques dans le cas d'un modèle de page Web car ils économisent des balises HTML. Ils impliquent toutefois une plus grande rigueur dans les CSS, comme nous le verrons dans la section suivante.
Voici le code HTML complet :
<!
DOCTYPE html
>
<html>
<head>
<link rel
=
"stylesheet"
href
=
"hello-bem.css"
>
</head>
<body>
<div class
=
"PageWrapper"
>
<header class
=
"SiteHeader"
>
<a class
=
"SiteHeader-titles"
href
=
"/"
>
<p class
=
"SiteHeader-h1"
>
Hello, World!</p>
<p class
=
"SiteHeader-h2"
>
BEM is sooo handy</p>
</a>
</header>
<div class
=
"BodyLayout"
>
<main class
=
"BodyLayout-mainContent"
>
<article class
=
"MainContent ob-formattedText"
><p>Main content here</p></article>
</main>
<div class
=
"BodyLayout-sidebar Sidebar"
>
<ul>
<li class
=
"Sidebar-li"
><aside class
=
"SmallBox sticky"
>
Widget 1</aside></li>
<li class
=
"Sidebar-li"
><aside class
=
"SmallBox"
>
Widget 2</aside></li>
</ul>
</div>
</div>
<footer class
=
"SiteFooter"
>
Something about copies here</footer>
</div>
Du texte formaté pourrait apparaitre dans plusieurs composants CSS différents, il fait donc l'objet d'une classe transversale ob-formattedText. Notons également la présence du modificateur sticky sur la première des deux instances du composant SmallBox.
VI-a. Discussion HTML▲
Deux sujets méritent une attention particulière. Premièrement, constatons que les arbres BEM et DOM ne coïncident pas en tout point. Nous avons des descendants frères : SiteHeader-titles, SiteHeader-h1 et SiteHeader-h2, tous trois sont des enfants du composant SiteHeader alors que dans l'arbre DOM les deux derniers sont des enfants du premier. Autre exemple : le descendant BodyLayout-sidebar est le parent du composant Sidebar dans l'arbre BEM alors que dans le DOM il s'agit du même élément.
Ensuite, certains morceaux de HTML peuvent être au choix des descendants ou des composants. Pour déterminer quel est le meilleur choix, la question n'est pas : « Cette chose-là a-t-elle du sens indépendamment du reste ? » L'en-tête d'un article, contenant le titre et la date de publication, perdrait son sens s'il était séparé du corps de l'article. Il mérite pourtant souvent d'être un composant car la question à se poser est plutôt : « A-t-on besoin de créer un contexte (d'apparence) à ce niveau ? » Et en effet, la zone de titre d'un article, surtout si elle est complexe, mérite d'être un composant. Cela permettra, par exemple, de la déplacer sous le corps de l'article le jour où ce dernier deviendra une vidéo.
En cas d'indécision, la règle que je suggère est de faire au plus simple. Dans le code HTML au-dessus, le descendant SiteHeader-titles pourrait être un composant SiteTitles. Dans la mesure où le composant englobant SiteHeader est presque vide, j'ai préféré utiliser son contexte. Mais le jour où nous lui ajouterons d'autres enfants, il deviendra plus pratique de créer un contexte composant à part pour les titres du site.
VII. Cas d'utilisation, partie 2 : CSS avec SASS▲
Dans cette partie, je vais utiliser le préprocesseur SASS avec sa variante syntaxique SCSS. SASS autorise les imbrications et depuis la version 3.3 sortie cette année elles fonctionnent même sur des noms composés.
En SASS, le & représente le sélecteur du bloc de déclarations parent. Un exemple de code SCSS :
.Sidebar
{
background-color:
#998
;
min-height:
160
px;
padding:
20
px 0
;
&-li {
margin-bottom:
10
px;
padding:
0
20
px;
}
}
… Après compilation par SASS, le code CSS généré est le suivant :
.Sidebar
{
background-color:
#998
;
min-height:
160
px;
padding:
20
px 0
}
.Sidebar-li
{
margin-bottom:
10
px;
padding:
0
20
px
}
Remarquez la réutilisation du sélecteur parent .Sidebar pour composer celui de l'enfant .Sidebar-li. Simple et nette.
Reprenons maintenant notre mix du descendant BodyLayout-sidebar et du composant Sidebar. Surtout ne vous laissez pas embrouiller. Cette explication est réellement facile. Il n'y a à chaque fois qu'un simple pattern de deux niveaux de hiérarchie, lequel se répète de manière imbriquée.
Dans l'arbre BEM, BodyLayout-sidebar est le conteneur de Sidebar. La règle CSS du composant Sidebar régit l'intérieur de la barre latérale, elle a été donnée plus haut. Le positionnement échoit au conteneur dont voici la règle :
La largeur du conteneur est définie en pourcentage : 25%. Celle du composant Sidebar est indéfinie, ce dernier prendra donc automatiquement la largeur allouée par le conteneur. Rappelons-nous que, dans le cas qui nous préoccupe, le composant et son conteneur sont en réalité le même élément HTML. Or, en CSS, un padding à gauche ou à droite, ou encore une bordure, s'ajoute à la largeur prise par l'élément HTML. Voilà pourquoi un mix demande de la rigueur : ici le composant ne doit pas utiliser ces propriétés CSS sous peine d'agrandir son propre conteneur.
Si l'on ne peut contrôler les règles CSS qui s'appliquent sur un composant, comme cela est le cas, notamment, dans une application JavaScript modulaire, alors mieux vaut éviter les mix en dissociant dans le DOM les descendants et les composants.
Le contenu complet du fichier hello-bem.scss :
@charset
"UTF-8"
;
// Reset CSS (partial)
html,
body,
div,
p,
a,
ul,
li,
footer,
header,
main {
border:
0
;
font:
inherit
;
margin:
0
;
padding:
0
;
vertical-align:
baseline
;
}
body {
line-height:
1
;
}
ul {
list-style:
none
;
}
// CSS Objects
.ob-formattedText
{
p {
margin-bottom:
.5em;
}
}
// CSS Components
body {
background-color:
#EEB
;
}
.PageWrapper
{
background-color:
#CCC
;
margin:
0
auto
;
width:
750
px;
}
.SiteHeader
{
height:
120
px;
position:
relative
;
&-titles {
display:
inline-block
;
left
:
80
px;
position:
absolute
;
top
:
20
px;
}
&-h1 {
font-size:
3
em;
font-weight:
bold
;
}
&-h2 {
font-size:
1.5
em;
font-style:
italic
;
}
}
.BodyLayout
{
&-mainContent {
float:
left
;
width:
73
%;
}
&-sidebar {
float:
right
;
width:
25
%;
}
&::
after {
clear:
both
;
content:
""
;
display:
block
;
}
}
.MainContent
{
background-color:
#FFF
;
min-height:
160
px;
padding:
20
px 40
px;
}
.Sidebar
{
background-color:
#998
;
min-height:
160
px;
padding:
20
px 0
;
&-li {
margin-bottom:
10
px;
padding:
0
20
px;
}
}
.SmallBox
{
background-color:
#665
;
color:
#fff
;
line-height:
50
px;
text-align:
center
;
&.sticky {
font-weight:
bold
;
}
}
.SiteFooter
{
line-height:
2
em;
text-align:
center
;
}
VII-a. Discussion CSS▲
Intéressons-nous tout d'abord à l'objet CSS ob-formattedText. Il est fait pour habiller des textes provenant d'un éditeur WYSIWYG. En situation réelle, il accueillera l'ensemble des règles d'affichage des éléments de formatage : du paragraphe aux listes à puces en passant par les sous-titres. Puisque les noms des balises HTML apparaissent directement, la séparation des contextes pour les éventuels sous-blocs est brisée. Cela n'est pas conforme à BEM mais il faut bien faire avec la réalité. Seuls des sous-blocs « résistants aux formatages » comme des médias devront être autorisés à l'intérieur de cet objet CSS.
Un mot encore à propos du positionnement. Le principe général est que les composants CSS et leurs modificateurs ne contiennent pas leur propre positionnement dans leur conteneur. Cela implique, la plupart du temps, de ne définir aucune marge sur l'élément HTML du composant (attention à la fusion des marges) et de ne toucher à aucune propriété qui influencerait le positionnement du composant dans son conteneur.
Ce principe n'est pas absolu. Le vrai principe sous-jacent est la réutilisation. Un positionnement est utilisé à bon escient dès qu'il fait partie intégrante du composant et ne gène donc aucunement sa réutilisation. Par exemple, le composant PageWrapper dans le code donné ci-dessus contient son propre positionnement centré. Ou encore, un composant InlineFig encapsulant une image avec sa légende, à l'intérieur d'un texte formaté, sera aligné en flottant à gauche ou à droite au moyen d'un modificateur :
Conclusion▲
Tant que sera respecté le principe d'une séparation rigoureuse entre les contextes des composants CSS, BEM fournira une robuste armature à nos pages Web et à nos applications JavaScript. Mais le Web autorise également des manipulations transversales puissantes. Les objets CSS décoratifs et les traitements en JavaScript travaillant en dehors de toute hiérarchie tirent parti des capacités si particulières des navigateurs, ne nous en privons pas.
Un parallèle parlera aux amateurs de langages à objets. Un composant CSS est un contexte, de la même manière qu'en programmation un objet en est un. À l'intérieur du composant, toutes sortes de descendants s'appuient sans façon les uns sur les autres, en programmation les membres privés d'un objet font de même. Vu depuis l'extérieur, le composant CSS apparait comme une « boite noire » réutilisable dont le mode d'implémentation n'importe plus. De telles considérations ont donné leur nom aux « objets CSS » de OOCSS.
Dans le cadre d'une application JavaScript, une bonne ergonomie fera toujours correspondre la ressemblance visuelle à une similitude du comportement programmé en JavaScript. Aussi, les règles CSS doivent être couplées au code JavaScript qui anime la portion du DOM concernée. Terminons alors sur les composants Web, ces briques de construction dans le domaine applicatif et non plus seulement visuel. Ils reposent sur quatre fonctionnalités utilisables séparément :
- les Templates — introduction, compatibilité ;
- le Shadow DOM — introductions 1, 2, 3, compatibilité ;
- les Custom Elements — introduction, compatibilité ;
- les HTML Imports — introduction, compatibilité.
Le support par les navigateurs progresse vite. Les quatre sont implémentées par Chrome et Opera, Firefox est en train de suivre, l'équipe d'Internet Explorer y réfléchit. Safari, en revanche, reste en dehors de la course.
Avec l'avènement du shadow DOM, certains nœuds du DOM deviennent des points de montage pour des portions de DOM encapsulées. Le DOM acquiert, en quelque sorte, du volume. La taille d'une portion de DOM ainsi encapsulée n'a pas de limite, un composant Web peut embarquer toute une application ! À l'intérieur des shadow DOM, de même bien entendu que dans le DOM principal du document, une syntaxe BEM reste appropriée. Du point de vue CSS, le shadow DOM est un espace de nommage pour les composants CSS embarqués.
En nous offrant de raisonner « par composants » à tous les niveaux de l'habillage en CSS, la méthodologie BEM et ses syntaxes préfixées s'intègrent naturellement dans un environnement fait de composants Web. Elles sont à la fois le présent et le futur des bonnes pratiques en CSS.