Sur la route des CSS maintenables
Introduction
- CSS est simple à apprendre
- CSS est difficile à maîtriser
- CSS est difficile à maintenir
It’s almost a challenge to find a development team that’s working on a codebase that’s more than a couple of years old where the CSS isn’t the most frightening and hated part of that system.
Les symptômes possibles
- Le temps de développement
- La taille des feuilles de style
- Le travail à plusieurs
- L'arrivée de nouveau collaborateurs
- Les travaux réservés aux expert
BEM, la solution !
BEM
- Rend vos CSS maintenables
- Soigne du cancer
- Ramène l'être aimé
- …
Le champs d'action des méthodologies CSS
.maintainable-selector { … }
.bem-selector { … }
Top Down CSS
.news .title { … }
#sidebar .news .title { … }
.widget .news .title,
#sidebar .widget .news .title { … }
Il devient rapidemment impossible de connaître toutes les règles déjà écrites
- On rajoute à la fin du fichier
- On rallonge le fichier
- On duplique
- On introduit des incohérences
- Les nouvelles ressources pleurent
On veut des styles
- Modulaires vs un seul fichier
on peut se concentrer sur un problème - Prévisibles vs sémantiques
on peut comprendre le CSS depuis l'HTML
Intégration modulaire
L'objectif est de se créer une collection de composants réutilisables.
Constat : L'HTML change constamment
On peut concevoir des feuilles CSS indépendantes de la structure HTML
Retour d'XP
Due to these efforts, we cut our average CSS bytes per page by 19% (after gzip) and HTML bytes per page by 44% (before gzip)
We started with 3348 lines of CSS and ended up with only 972, meaning we managed to delete more than 2/3 thanks to deleting unused selectors, merging similar styles, and employing CSS re-usability techniques like mixins, variables, and functions.
Factorisation de 3 sites de jeu en ligne, dans le but de créer une marque blanche. On est passé de +24 000 lignes de CSS à -4000 lignes. En 1 mois.
Des sélecteurs au régime
Pas d'id
/* Header object */
#header { background-color: #464E51; }
#header a { color: hotpink; }
- Limite la réutilisabilité
- Trop spécifique
Pas de balise
/* Button object */
a.button { color: black; }
<button type="button" class="button"></button>
<a href="#" class="button"></a>
<input type="button" class="button" />
- Limite la réutilisabilité
Pas de balise
/* Header object */
.header a { color: white; }
/* Button object */
.button { color: black; }
- Évite les fuites de styles
Eviter l'imbrication
/* Article object */
.article .title { font-weight: bold; }
/* Comment object */
.comment .title { color: hotpink }
<div class="article">
<div class="comment">
<div class="title"></div>
</div>
</div>
- Évite les fuites de styles
Eviter l'imbrication
<div class="article">
<div class="title"></div>
<div class="comment">
<div class="title"></div>
</div>
</div>
- Prévisibilité
Eviter l'imbrication, ou utiliser l'opérateur >
/* Article object */
.article > .title { font-weight: bold; }
/* Comment object */
.comment > .title { color: hotpink }
- Évite les fuites de styles
- Mais non prévisible !
Pas de +
, ~
, :nth-child()
, etc.
<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
- Prévisibilité
- Complexité
En conclusion
- Le sélecteur idéal est une seule classe
- Le CSS ne contient aucune logique
- Le style qui s'applique est prévisible
La classe
ne fait pas le module
.account-button
.account-content-button
.auth-content-form__input
Nommer le visuel ou la fonction visuelle
.highlight-box
vs.bundle-product-discount-box
.primary-button
vs.submit-contact-form-button
BEM
La notation BEM permet une rapide indentification du rôle d'une classe
Ils utilisent BEM sur leur site principal
Et Google, Twitter, BBC, theguardian, Financial Time, etc.Block
Un block est
- Un composant visuel ou fonctionnel
- Indépendant de la page et des autres blocks
- La racine d'une feuille style
- La racine d'une logique fonctionnelle
Ils sont indépendants
Ils peuvent être réutilisés
Il peuvent être imbriqués
On le cible à l'aide d'une seule classe
.block-name { … }
.c-block-name { … }
.tzi-block-name { … }
.tzi-c-block-name { … }
Par exemple
.c-header { … }
.c-logo { … }
.c-search-form { … }
.c-auth-form { … }
.c-top-menu { … }
Chaque block a son propre fichier
Les sélecteurs sont la clé de la maintenabilité
Element
Un élément est une entité BEM représentant une portion d'un seul et même block.
Il ne peut pas être utilisé en dehors du contexte de son block référant.
On le cible à l'aide d'une seule classe
.block-name__element-name { … }
.c-block-name__element-name { … }
.tzi-block-name__element-name { … }
.tzi-c-block-name__element-name { … }
Par exemple
.c-search-form { … }
.c-search-form__input { … }
.c-search-form__button { … }
.c-search-form__suggestion-dropdown { … }
Modifier
Un modifier est une entité BEM représentant l'altération de l'apparence d'un bloc ou d'un élément.
Il s'agit du même concept que les classes d'extension en OOCSS.
L'utilisation des modifiers en BEM est facultative.
On le cible à l'aide d'une seule classe
.block-name--modifier-name { … }
.block-name__element-name--modifier-name { … }
.c-block-name--modifier-name { … }
.tzi-block-name--modifier-name { … }
.tzi-c-block-name--modifier-name { … }
Par exemple
.c-top-menu { … }
.c-top-menu--align-left { … }
.c-top-menu__tab { … }
.c-top-menu__tab--active { … }
Donner du sens aux classes
Les noms de classe peuvent être ambigus
<form class="search full">
<input class="input dropdown" name="query" type="search">
<button class="button" type="submit">
Search
</button>
</form>
En BEM, on enlève les doutes possibles
<form class="c-search-form c-search-form--full">
<input class="c-search-form__input js-dropdown" name="query" type="search">
<button class="c-button" type="submit">
Search
</button>
</form>
On peut même expliciter les imbrications
<form class="c-search-form c-search-form--full">
<input class="c-search-form__input js-dropdown" name="query" type="search" >
<div class="c-search-form__action">
<button class="c-button" type="submit">
Search
</button>
</div>
</form>
Parfois les imbrications peuvent être discrètes
<form class="c-search-form c-search-form--full">
<input class="c-search-form__input js-dropdown" name="query" type="search" >
<button class="c-search-form__action c-button" type="submit">
Search
</button>
</form>
Reprennons le module media de OOCSS
<div class="media">
<img class="img-rev" src="logo.png" alt="Foo Corp logo">
<div class="body">
<h3 class="title">Welcome to Foo Corp</h3>
</div>
</div>
Là encore, les classes BEM sont explicites
<div class="media">
<img class="media__img media__img--rev" src="logo.png" alt="Foo Corp logo">
<div class="media__body">
<h3 class="title">Welcome to Foo Corp</h3>
</div>
</div>
Conseils
One of the hardest parts of BEM is deciding when to start and stop scope, and when (or not) to use it
Don’t Layout Yourself: A component should style itself, but give up the task of layouting to its parent.
N'hésitez pas à
- Découper
- Imbriquer
- Dupliquer (oui...)
Dry n'est pas le copain de BEM
N'abuser pas de Sass
- Pas de génération de sélecteur
- Pas de
@extend
massif - Limiter aux mixins pratiques
@include clearfix();
@include text-ellipsis();
Vous pouvez l'utiliser partout