Écrire du CSS est relativement facile mais garder le code fonctionnel et propre sur le long terme est beaucoup plus difficile - d’ajout en ajout, le code devient de moins en moins compréhensible.
CSS difficile à comprendre
Prenons le code suivant:
<div class="box profile pro-user">
<img class="avatar image" />
<p class="bio">...</p>
</div>
Il est impossible de savoir si les classes box
et profile
sont liées,
s’il est possible d’utiliser avatar
en dehors de profile
,
si la classe pro-user
est utilisée ailleurs qu’ici, etc.
CSS difficilement réutilisable
Supposons qu’il existe un style sur une page que vous souhaitez réutiliser sur une autre page,
mais lorsque vous l’essayez, vous découvrez que le code a été écrit d’une manière qui ne fonctionne que sur la première page. L’auteur a supposé que l’élément serait placé dans un parent particulier, et cela ne fonctionne pas dans un autre contexte. Vous dupliquez donc le code.
CSS difficile à surcharger
Vous voulez mettre à jour le style d’un élément sur une page en particulier et il rompt sur une autre. Vous essayez de modifier le style sur l’autre page, mais vous vous retrouvez pris dans une guerre de spécificité.
C’est pour éviter ces problèmes qu’il existe des guidelines pour structurer le CSS.
Il existe différentes conventions, les trois principales sont OCCSS, BEM et SMACSS.
Toutes s’articulent autour d’une idée de base: écrire le CSS de manière modulaire.
On a tendance à penser à une page avec une approche top-down: un bandeau haut, une barre de navigation verticale, une liste d’articles, etc. Le problème est que cela conduit à écrire du code qui n’est pas réutilisable.
Le principe du CSS modulaire est de prendre du recul et de décomposer la page en morceaux de blocs discrets: un logo, une barre de recherche, des onglets, une liste de photos, un lecteur vidéo, etc. Ces éléments peuvent être utilisés sur différentes pages à différents endroits. Il se trouve juste qu’ils sont assemblés de cette manière sur cette page mais qu’ils pourraient être disposés autrement sur une autre page.
Écrire du CSS de manière modulaire requiert de réfléchir avec une approche bottom-up: on commence avec des blocs réutilisables puis on les dispose, un peu comme des legos.
OOCSS, pour Object-Oriented CSS, a été créé en 2009 par Nicole Sullivan suite à son travail chez Yahoo. C’est le point d’origine du CSS modulaire.
Éléments indépendants du contexte:
Le concept de base est que les objets sont des motifs réutilisables dont l’apparence n’est pas déterminée par le contexte.
Par exemple, plutôt que de rendre tous les boutons du menu de navigation oranges et ceux de la page bleus, on utilise une classe pour déterminer la couleur du bouton.
Classes réutilisables:
Utiliser des classes réutilisables pour les modèles visuels courants.
Par exemple sur Amazon en 2009, il y avait des ombres portées sur presque tout, et elles étaient toutes un peu différentes. En normalisant ces ombres portées, vous pouvez optimiser le code et rendre le site plus performant.
Ne pas utiliser d’ID pour sélecteur:
Un ID est unique par page. Utiliser des ID pour sélecteur empêche de pouvoir réutiliser le code ailleurs et est donc à éviter.
Ne pas utiliser de balise HTML pour sélecteur:
Il y a plusieurs raisons de préférer les classes aux balises:
<a>
plutôt qu’un <button>
, il devrait être possible d’appliquer le même style sans avoir à modifier le CSS..logo
est plus facile à comprendre que header a
.Note: On peut en revanche de combiner les classes et les balises, par exemple .markdown h2
.
Exemple OOCSS:
<div class="media">
<a href="#" class="img">
<img src="" alt="" />
</a>
<div class="bd"></div>
</div>
.media {}
.media .img {}
.media .img img {}
.media .imgExt {}
.bd {}
BEM, pour Block, Element, Modifier, a été en 2009 à Yandex (moteur de recherche et serveur email Russe).
Le concept de base est le suivant:
une page est composée blocs
Les blocs sont indépendants des page et ils peuvent être imbriqués les uns dans les autres.
Par exemple, vous pouvez avoir un bloc qui est une liste d’onglet et placer à l’intérieur de ce bloc des boutons, qui est un type de bloc distinct. Les styles des deux blocs n’interagissent pas, il se trouve juste qu’il y en a un imbriqué dans l’autre.
les blocs contiennent des éléments
Les éléments sont les parties constituantes d’un bloc et ils ne peuvent pas être utilisées en dehors.
Par exemple, les items d’un menu de navigation —
cela n’a pas de sens de créer un bloc pour un item du menu: on crée un bloc pour le menu lui-même et les items du menu sont des éléments enfants.
les blocs ou les éléments peuvent être modifiés
Les modificateurs définissent l’apparence et le comportement d’un bloc.
Par exemple, le fait que le menu soit horizontal ou vertical ou qu’un bouton est bleu ou orange.
BEM définit la convention de nommage suivante:
.block-name__element--modifier
Par exemple:
<button class="btn btn--big btn--orange">
<span class="btn__price">$9.99</span>
<span class="btn__text">Subscribe</span>
</button>
Notez que cette recommendation à changé en 2017: le double-tiret est remplacé par un simple underscore, et ce, pour pouvoir écrire des noms de classe dans les commentaires HTML — qui n’autorisent pas l’usage des doubles-tirets.
.block-name__element_modifier
Release 2.3.1 - May 25, 2017
BEM Naming Change
Initially we used the double dash style for BEM notation (–). Due to the fact that double dashes are problematic in an XML environment (which doesn’t allow double dashes within comments), we have upgraded our syntax to use the single underscore style (_). This change is backward compatible for 18 months. But all components going forward are built with the single underscore BEM style.
Les guidelines données par BEM sont les suivantes:
Utiliser des tirets pour les noms de bloc
En JavaScript, utiliser du camelCase pour nommer les variables est une pratique courante.
Cela permet de rester cohérent avec les méthodes Javascript, qui sont écrites elles aussi en camelCase.
var redBox = document.getElementById('...');
De la même manière, BEM préconise d’utiliser des tirets en CSS. Cela permet rester cohérent avec les propriétés CSS et d’éviter de mixer deux notations.
.some-class {
font-weight: 10em;
}
Séparer les sélecteurs CSS des sélecteurs JavaScript.
Il existe différentes conventions pour parvenir à ce but:
Préfixer les noms de classe utilisé dans le code JavaScript par js-
, de cette manière toute personne comprend immédiatement pourquoi cette classe existe.
<div class="site-navigation js-site-navigation">
Une autre alternative est d’utiliser l’attribut rel
pour définir la relation entre un élément HTML et JavaScript.
<div class="site-navigation" rel="js-site-navigation">
Ne pas utiliser les attributs data-
comme hook JavaScript. Par définition, les attributs data permettent de stocker des données personnalisées, il est préférable de distinguer les deux.
<li class="js-stream-item stream-item"
data-item-id="9417"
data-item-type="tweet"
id="stream-item-tweet-9417"></li>
Utiliser des sélecteurs simples.
Les sélecteurs doivent toujours être suffisamment spécifiques pour ne pas avoir à être imbriqués. Par exemple, on utilise .btn__price
et non .btn .btn__price
.
Utiliser des mixes de sélecteurs pour éviter de dupliquer le code CSS.
Par exemple, pour appliquer une ombre portée ou le style d’un lien, utiliser une classe spécifique plutôt que de dupliquer le CSS.
<nav class="menu">
<a class="link menu__item" href=""></a>
<a class="link menu__item" href=""></a>
<a class="link menu__item" href=""></a>
</nav>
Exemple BEM:
<div class="media media--inverted">
<a href="#" class="media__image-wrapper">
<img class="media__image" src="" alt="" />
</a>
<div class="media__content"></div>
</div>
.media {}
.media--inverted {}
.media__image-wrapper {}
.media__image {}
.media__content {}
Équivalent en SCSS:
.media {
&--inverter {}
&__image-wrapper {}
&__image {}
&__content {}
}
Voir aussi: Official BEM Methodology Quick Start
SMACSS, pour Scalable & Modular Architecture for CSS, a été crée en 2011 par Jonathan Snook suite à son travail chez Yahoo Mail. SMACSS ajoute le concept de catégories de composants. Il existe 5 catégories:
Base
Les styles de base définissent l’apparence d’un élément n’importe où sur la page. Cela garantit que les styles soient les mêmes d’un navigateur à l’autre. Pour sélectionner des éléments de base, on utilise les balises HTML.
html {
margin: 0;
font-family: sans-serif;
}
a {
color: #000;
}
button {
color: #ababab;
border: 1px solid #f2f2f2;
}
Layout
Les styles de layout divisent la page en sections principales: bandeau haut, bandeau bas, navigation verticale, contenu. Pour sélectionner des éléments qui constituent le layout, on utilise les ID.
Les éléments à l’intérieur du layout peuvent être placés (bordures, alignements, marges, etc).
Les classes permettant de placer les éléments sont préfixées par la lettre l-
.
#header {
background: #fcfcfc;
}
#header .l-right {
float: right;
}
#header .l-align-center {
text-align: center;
}
On peut utiliser ces classes partout dans la page, toujours en utilisant le préfixe l-.
.l-full-width {
width: 100%;
}
Module
À l’intérieur des éléments de layout sont placés les modules, tels que des boîtes, des cartes, des listes, des galeries, etc. Les modules sont réutilisables partout dans le page, même principe que BEM.
En SMACSS, pour distinguer différents éléments dans différents contextes, on utilise deux tirets:
<div class="box">
<div class="box--label">This is box label</div>
<ul class="box--list list">
<li class="list--li">Box list element</li>
</ul>
</div>
.box--label {
color: blue;
}
.card--label {
color: red;
}
State
Les styles d’état décrivent comment le module s’affiche dans des situations dynamiques: caché ou ouvert, désactivé, sélectionné, etc. Les classes sont préfixées de is-
.
<header id="header">
<ul class="nav">
<li class="nav--item is-selected">Contact</li>
<li class="nav--item">About</li>
</ul>
</header>
.nav--item.is-selected {
color: #fff;
}
Theme
Les styles de thème sont utilisés pour définir les couleurs, formes, bordures, ombres, etc.
<button class="button-large">Like</button>
.button-large {
width: 60px;
height: 60px;
}
Toutes ces conventions de nommage sont différentes mais similaires.
De manière générale, on distingue 3 types d’items
Les styles des éléments peuvent être organisés en différentes catégories:
les règles de base définissent le style par défaut des éléments HTML.
a
, li
, h1
les règles de layout définissent comment les éléments sont placés mais pas leur apparence visuelle.
.l-centered
, .l-grid
, .l-fixed-top
les règles des modules définissent l’apparence des éléments.
.m-profile
, .m-card
, .m-modal
les règles d’état sont ajoutées par JavaScript.
.is-hidden
, .is-collapsed
, .is-active
les règles utilitaires (helpers) modifient l’apparence d’un élément.
.h-uppercase
, .h-nowrap
, .h-muted
Les principales conventions à respecter sont
Source: What is Modular CSS?
De la même manière que les sélecteurs CSS, vos variables CSS (natives ou celles de préprocesseurs comme Sass ou Less) devraient être sémantiques et faciles à comprendre. Il n’existe pas de préconisation sur le sujet mais pour rendre vos variables facilement compréhensible, vous pouvez suivre le format suivant:
type-importance
type:
par exemple clr
pour les couleurs, fs
pour les tailles de polices, grid
pour les tailles de grilles, etc.
importance:
base
pour la valeur par défaut (du corps de texte), puis de manière logique primary
/secondary
ou alpha
/beta
ou encore giga
/micro
suivant le type de variable.
La plupart des marques ont une couleur dominante pour leur marque, puis des couleurs subsidiaires qui complètent cette couleur.
Outre les couleurs de la marque, il y a couramment des couleurs neutres utilisées pour former la base de tout contenu.
Une couleur particulière peut avoir une gamme de teintes et de nuances, par exemple une couleur de fond, une couleur pour l’écriture et une pour la bordure.
En gardant ces éléments en têtes, on peut constituer une liste de noms de variables de couleur facilement compréhensibles:
/* couleur de base, avec une teinte et une nuance
- typiquement utilisé pour la couleur fond du site et la typographie */
$clr-base: #666;
$clr-base-lt: #999;
$clr-base-dk: #333;
/* couleur dominante - skyblue
- avec des teintes et nuances via des fonctions Sass */
$clr-primary: skyblue;
$clr-primary-lt: lighten($clr-primary, 5%);
$clr-primary-ltr: lighten($clr-primary, 10%);
$clr-primary-dk: darken($clr-primary, 5%);
$clr-primary-dkr: darken($clr-primary, 10%);
/* couleur secondaire - hotpink
- avec des teintes et nuances via des fonctions Sass */
$clr-secondary: hotpink;
$clr-secondary-lt: lighten($clr-secondary, 5%);
$clr-secondary-ltr: lighten($clr-secondary, 10%);
$clr-secondary-dk: darken($clr-secondary, 5%);
$clr-secondary-dkr: darken($clr-secondary, 10%);
/* couleurs neutres */
$clr-ntrl-min: #fff;
$clr-ntrl-max: #000;
Toutes les variables de couleur sont préfixées de clr-
.
Les couleurs de la marque sont ordonnées par ordre d’importance: primary
, secondary
, tertiary
, quaternary
, etc.
lt
pour light,ltr
por lighter,ltst
pour lightest, etc.$clr-success
, $clr-warning
, etc, en ajoutant les teintes et nuances de ces couleurs toujours sur le même schéma.Toutes les variables de taille de police sont préfixées de fs-
(pour font-size) puis sont ordonnées de grandes tailles (pour le logo par exemple) à petites tailles (pour les éléments sub par exemple).
/* taille de base - appliqué au contenu de la page */
$fs-base: 16px;
/* tailles plus grandes que les éléments de heading */
$fs-giga: 80px;
$fs-mega: 70px;
$fs-kilo: 60px;
/* tailles des éléments de heading */
$fs-h1: 36px;
$fs-h2: 32px;
$fs-h3: 28px;
$fs-h4: 24px;
$fs-h5: 20px;
$fs-h6: 18px;
/* tailles plus petites que le corps de texte */
$fs-milli: 14px;
$fs-micro: 10px;
$fs-nano: 8px;
Toutes les variables de famille de police sont préfixées de ff-
(pour font-family) puis sont ordonnées par ordre de prépondérance: contenu, titre, sous-titre, icone.
$ff-base: 'Helvetica Neue', Helvetica, Arial, sans-serif;
$ff-alt-alpha: 'Open Sans', sans-serif;
$ff-alt-beta: 'Roboto', sans-serif;
$ff-icon: 'My Icon Font';
Source: Make Your CSS Variable Names Suck Less
La convention de nommation de Material Design pour Android est comme suit:
<style name="Theme.MyApp" parent="Theme.MaterialComponents.*">
<!-- Couleur -->
<item name="colorPrimary">#6200EE</item>
<item name="colorPrimaryVariant">#3700B3</item>
<item name="colorOnPrimary">#FFFFFF</item>
<item name="colorSecondary">#03DAC6</item>
<item name="colorSecondaryVariant">#018786</item>
<item name="colorOnSecondary">#000000</item>
<item name="colorError">#B00020</item>
<item name="colorOnError">#FFFFFF</item>
<item name="colorSurface">#FFFFFF</item>
<item name="colorOnSurface">#000000</item>
<item name="colorBackground">#FFFFFF</item>
<item name="colorOnBackground">#000000</item>
<!-- Typographie -->
<item name="textAppearanceHeadline1">Light 96sp</item>
<item name="textAppearanceHeadline2">Light 60sp</item>
<item name="textAppearanceHeadline3">Regular 48sp</item>
<item name="textAppearanceHeadline4">Regular 34sp</item>
<item name="textAppearanceHeadline5">Regular 24sp</item>
<item name="textAppearanceHeadline6">Medium 20sp</item>
<item name="textAppearanceSubtitle1">Regular 16sp</item>
<item name="textAppearanceSubtitle2">Medium 14sp</item>
<item name="textAppearanceBody1">Regular 16sp</item>
<item name="textAppearanceBody2">Regular 14sp</item>
<item name="textAppearanceCaption">Regular 12sp</item>
<item name="textAppearanceButton">Medium all caps 14sp</item>
<item name="textAppearanceOverline">Regular all caps 10sp</item>
<!-- Forme -->
<item name="shapeAppearanceSmallComponent">4dp rounded</item>
<item name="shapeAppearanceMediumComponent">4dp rounded</item>
<item name="shapeAppearanceLargeComponent">0dp rounded</item>
</style>
La valeur par défaut des attributs de typographie et de forme a été abrégé en une ligne.
La structure interne est comme suit:
<style name="TextAppearance.MyApp.Headline1" parent="TextAppearance.MaterialComponents.Headline1">
<item name="fontFamily">@font/custom_font</item>
<item name="textStyle">normal</item>
<item name="textAllCaps">false</item>
<item name="textSize">64sp</item>
<item name="letterSpacing">0</item>
</style>
<style name="ShapeAppearance.MyApp.SmallComponent" parent="ShapeAppearance.MaterialComponents.SmallComponent">
<item name="cornerFamily">cut</item>
<item name="cornerSize">4dp</item>
</style>
Liens utiles: