J'ai testé pour vous

Trello's blue logo

CSS Guidelines


Enfin une solution CSS pratique ?

@BastienTanésie / Wixiweb

Le bon plan

  1. Pourquoi ces guidelines ?
  2. Les guidelines
    1. Tools
    2. Components
    3. Javascript
    4. Mixins
    5. Utilities
    6. File structure
  3. Retour d'expérience

Pourquoi ces guidelines ?

  • Les CSS, c'est le bordel depuis le début
  • !important et inline, on en parle ?
  • Mélange classes et ids
  • CSS vs. JS
  • « Cette classe est encore utilisée ? »

Tools


Bob le Bricoleur à la rescousse du CSS

Tools

Use only imports, variables, and mixins (and only for vender-prefixed features) from CSS preprocessors
  • Lisibilité et simplicité du code
  • Vanilla CSS autant que faire se peut
  • Pas de fonctionnalités avancées des pré-processeurs
    Nesting abusif, boucles, etc.

Components


Moins de spécificité, c'est la clé du succès

Components

Use the .component-descendant-descendant pattern for components
  • Évite les cascades de règles
  • Améliore la maintenabilité
  • Utiliser des namespaces

Components

Pas de "sélecteur déscendant" :


.global-header {}

.global-header .logo {}

.global-header .logo img {}
						

Utilisation du namespacing :


.global-header {}

    .global-header-logo {}
    
        .global-header-logo-image {}
						

Components

Spécificité la plus basse possible,
évite les !important et le style inline

  • Tous les sélecteurs doivent être des classes aucun ID ou élément
  • Pas d'underscores ni de camelCase ou autre joyeuseté
  • Minuscules, séparés par des tirets
  • Indentation en fonction de la descendance
  • États et pseudo-classes sur la même colonne

Components

— Modifiers —

Components : Modifiers

Use the .component-descendant.mod-modifier pattern for modifier classes

Besoin de transformer un élément dans un style particulier.

Exemple: un message en mode "erreur" ou "succès".

La base est commune (un bloc de message),
mais change visuellement selon le but :
Alerter, valider, informer, etc.

Components : Modifiers


Message de succès
Message d'erreur

.alert-message {}
.alert-message.mod-success {}
.alert-message.mod-error {}
						

Components : Modifiers

Ne JAMAIS déclarer un modifier seul !

Un modifier est toujours rattaché à un composant.


Permet l'utilisation d'un modifier de même nom sur plusieurs composants.

Exemple : .mod-success sur un message et un bouton

Components : Modifiers

Si besoin d'écraser le style d'un déscendant,
utiliser exceptionnelement le nesting.


.global-header-nav-item.mod-sign-up {
    background: hsl(120, 70%, 40%);
    color: #fff;
    
    .global-header-nav-item-text {
        font-weight: bold;
    }
}
						

Components

— States —


Pas forcément unis

Components : States

Use the .component-descendant.is-state pattern for state.
Manipulate .is- classes in JavaScript (but not presentation classes).

Gestion des états spécifiques des composants.


Exemple: .is-loading, .is-disabled, .is-hidden, etc.


Séparation des états et de la présentation.

Components : States

Exemple du logo de Trello en mode "chargement" :


.global-header-logo-image {
    background: url("logo.png");
    height: 40px;
    width: 200px;
}

.global-header-logo-image.is-loading {
    background: url("logo-loading.gif");
}
						

En BEM : .global-header-logo-image--is-loading


  • Dev : Tiens, cet élément a une classe .is-loading pour le status "en chargement", à toi de jouer !
  • Designer : Cool, merci ! J'vais me débrouiller avec ça :)

Components : States

Chaque composant définit ses propres états

Jamais de .is- sans composant ciblé !


Donc
.is-hidden { display: none; }
=
« Permit denied »

Components

— Media Queries —


Fini le casse-tête des break-points

Components : Media Queries

Use media query variables in your component.

Regrouper toutes les Media Queries dans un seul fichier.

Chaque Media Query (ou break-point) est stockée dans une variable.

Components : Media Queries

Déclaration des variables


@highdensity:  ~"only screen and (-webkit-min-device-pixel-ratio: 1.5)",
               ~"only screen and (min--moz-device-pixel-ratio: 1.5)",
               ~"only screen and (-o-min-device-pixel-ratio: 3/2)",
               ~"only screen and (min-device-pixel-ratio: 1.5)";

@small:        ~"only screen and (max-width: 750px)";
@medium:       ~"only screen and (min-width: 751px) and (max-width: 900px)";
@large:        ~"only screen and (min-width: 901px) and (max-width: 1280px)";
@extra-large:  ~"only screen and (min-width: 1281px)";

@print:        ~"print";
						

Components : Media Queries

Utilisation des Media Queries dans les composants


/* Input */
@media @large { 
    .component-nav {}
}

/* Output */
@media only screen and (min-width: 901px) and (max-width: 1280px) {
    .component-nav {}
}
						

Components : Media Queries

On peut améliorer les perfs en regroupant/compressant toutes les Media Queries

avec css-mqpacker par exemple

npm install css-mqpacker --save-dev

Components

— Keeping It Encapsulated —


Spoiler : on ne parlera même pas de café…

Components : Encapsulate

Un composant dans un autre : compo-ception !


La règle

Un composant ne doit rien connaître des autres

Components : Encapsulate

Exemple : une liste et un bouton

Besoin : modifier la taille et la position du bouton

Ce qu'il ne faut pas faire


Pat

Add

// components/member-list.less
.member-list-item-button {
    float: right;
    padding: 6px 10px;
}
				        

Components : Encapsulate

Ce qu'il faut faire :


Gumby

Add

Components : Encapsulate

Beaucoup de composants, c'est normal

Ne pas hésiter à éclater les gros composants en plusieurs plus petits


Beaucoup de modifiers et/ou de déscendants ?
Il est temps de splitter !

Javascript


On ne mélange pas les torchons et les serviettes

Javascript

Separate style and behavior concerns by using .js- prefixed classes for behavior.

Séparation de la logique (JS) et de la présentation (CSS).

Rappel : ne jamais utiliser d'ID en JS

Javascript

Utiliser des noms de classes clairs, qui ont du sens :

.js-open-content-menu au lieu de .js-menu


Ne jamais avoir un .js- dans un fichier CSS

Mixins

Mixins

Prefix mixins with .m- and only use them sparingly for shared styles.

Pour des styles partagés entre plusieurs composants

Pour les vendor-prefixes, on peut utiliser autoprefixer à la place


// mixins.less
.m-list-divider () {
    border-bottom: 1px solid @light-gray-300;
}
// component.less
.component-descendent {
    .m-list-divider();
}
						

Utilities

Utilities

Prefix utility classes with .u-.

clearfix, alignement vertical, styles de texte, etc.



.u-truncate-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
						

Tout ça dans un seul et même fichier.


Pas de .u-float-left, directement dans le composant

File structure


Mais comment qu'on organise tout ça ?

File structure


@charset "UTF-8"
@import "normalize.css"
// Variables
@import "media-queries.less"
@import "colors.less"
@import "other-variables-like-fonts.less"
// Mixins
@import "mixins.less"
// Utils
@import "utils.less"
// Components
@import "component-1.less"
@import "component-2.less"
@import "component-3.less"
@import "component-4.less" // and so forth
						

File structure

Si c'est bien fait, pas besoin d'organiser les composants

Comprennez : l'ordre des composants n'a pas d'importance


En sortie : un seul fichier CSS

Facile à compresser avec un task-runner à la mode

Bonus


L'ordre des classes a son importance

component mod util state js

Retour d'expérience


Comme si j'avais vraiment de l'expérience…


— The End —

Merci

Sources