Méthodologies et conventions

CSS

conventions de nommage, BEM, Tailwind, etc.

Tailwind

2017

BEM

2010

Évolution des méthodologies CSS

un peu d'histoire

OOCSS

2008

PostCSS

2013

Sass

2006

LESS

2009

Atomic CSS

2013

ITCSS

2014

CSS-in-JS

2014

Bootstrap

2011

SMACSS

2011

Au programme

"un peu d'organisation, diantre !"

  1.  Les problématiques à résoudre

  2.  Appliquer une méthodologie (BEM, Atomique)

  3.  Du design à l'intégration

Les principales

Problématiques

à résoudre

La Cascade CSS

1

!important, transitions, animations

styles utilisateur, styles navigateur, styles du webdeveloppeur

règles de spécificité (poids) des sélecteurs

ordre d'affichage (le dernier écrase le précédent)

1

Inconvénients majeurs :

  1. Chaque type de sélecteur CSS a sa propre spécificité
  2. Ajouter du poids pour écraser un sélecteur a un effet "boule de neige" pour la prochaine modification à opérer
  3. Pourquoi ce ☠️ 🤬 😡 de bouton ne devient pas rose ?!?!

Solutions envisageables :

  1. Connaître la cascade et la spécificité des sélecteurs
  2. Employer des astuces modernes (@layer, :where, @scope)
  3. Choisir des sélecteurs légers (.class et non #id)
  4. C'est le principal job des méthodologies telles que BEM ou Atomique

La Cascade CSS

ul#navigation > .link { color: salmon }
ul#navigation li a { color: pink }
#navigation a.link { color: olive }
body ul#navigation li a { color: tomato }
body nav ul li a.link:first-child { color: chocolate }

Nommage des éléments

2

.img, .image, .blog-img, .img-blog, .blogImage, .thumbnail_img { ... }
.btn, .bouton, .button, .btn--primary, .close-btn, .btn-close { ... }
.wrapper, .container, .items-container, .inner-wrapper, .wrapper__inner--alternate { ... }
.title-main, .post-title, .heading { ... }

Inconvénients majeurs :

  1. Difficile de trouver des noms cohérents au fur et à mesure que le projet grossit
  2. Risque de choisir un nom déjà existant et d'écraser une partie des styles

Solutions envisageables :

  1. Avoir une convention de nommage (français/anglais, camelCase, BEM, etc.)
  2. Préfixer les noms (.l-wrapper, .c-button)
  3. Générer des noms dynamiquement (.abc23f4, .xyr421337) à l'aide d'un outil

Duplication des sélecteurs

3

.wrapper { }
.wrapper h1, .wrapper h2, .wrapper p { }
.wrapper:hover, .wrapper:focus { }
.wrapper::before { }

@media (width > 640px) {
  .wrapper { }
  .wrapper::before { }
}

Inconvénients majeurs :

  1. Difficile de trouver, renommer, déplacer, supprimer les sélecteurs

Solutions envisageables :

  1. Nesting Scss pour réduire les duplications
  2. Nesting en CSS natif

... mais attention au nesting multiple !

.wrapper {
  & h1, 
  & h2, 
  & p {}
  
  &:hover,
  &:focus {}
  
  &::before {}
  
  @media (width > 640px) {
    &::before {}
  }
}

Duplication des règles CSS

4

.button-primary {
  display: inline-block;
  padding: 1rem 2rem;
  border: 1px solid hotpink;
  background: pink;
  cursor: pointer;
}
.button-secondary {
  display: inline-block;
  padding: 1rem 2rem;
  border: 1px solid chocolate;
  background: bisque;
  cursor: pointer;
}

Inconvénients majeurs :

  1. Code inutilement répété
  2. Maintenabilité complexe

Solutions envisageables :

  1. Rassembler les styles identiques
  2. Proposer des classes (variantes) complémentaires
.button {
  display: inline-block;
  padding: 1rem 2rem;
  cursor: pointer;
}
.button-primary {
  border: 1px solid hotpink;
  background: pink;
}
.button-secondary {
  border: 1px solid chocolate;
  background: bisque;
}

Code mort

5

/* on préfère ne rien supprimer */
a.special {color: tomato;}
a.alternate {color: pink;}

Inconvénients majeurs :

  1. Le code inutilisé s'accumule au fur et à mesure que le projet grossit 
  2. Il pollue les feuilles de styles et les rend difficilement compréhensibles

Solutions envisageables :

  1. Détection des codes inutilisés (dans les DevTools)
  2. Suppression des codes inutilisés (unusedCSS, PurgeCSS)
<!-- lien à remplacer par un button -->
<a class="alternate special">
  Cliquez-moi !
</a>

Les Méthodologies

OK, alors on fait quoi ?

la Méthodologie "BEM"

Une Convention de nommage à toute épreuve !

Méthodologie "BEM"

Block - Element - Modifier

  1. Block
    entité indépendante et autonome
  2. Element
    entité dépendante de son Block
  3. Modifier
    variante d'un Block ou d'un Element
<p class="author__bio--current">

Méthodologie "BEM"

une convention de nommage... contraignante

.card

Nommage des blocks :

.contact
.post
.main-navigation
.card__title

Nommage des éléments :

.post__author

Règle : block name + __ + element name

.post__date
.card__title--disabled

Nommage des modifieurs :

Règle : block name OU element name + -- + modifier name 

.post--important

Méthodologie "BEM"

les principes essentiels

Tous les éléments HTML doivent chacun avoir un nom, sous forme de classe(s) CSS (pas de nommage via id)

1

2

3

Les sélecteurs CSS ne doivent cibler que des classes (pas de sélecteur nav, ni a, ni ul par exemple)

Les sélecteurs CSS composés sont à éviter (pas de .menu .list, ou de .navigation > a)

<p class="author__bio--current">

Frameworks "BEM" : Bootstrap (en partie), bemskel, BULMA

Méthodologie "BEM"

concrètement ?

<nav class="navigation">
  <ul>
    <li><a href="" class="active"></a></li>
    <li><a href=""></a></li>
    <li><a href=""></a></li>
    <li><a href=""></a></li>
  </ul>
</nav>

aheum, ça c'est pas du BEM hein ?

<nav class="navigation">
  <ul class="navigation__list">
    <li class="navigation__list-item"><a href="" class="navigation__link navigation__link--active"></a></li>
    <li class="navigation__list-item"><a href="" class="navigation__link"></a></li>
    <li class="navigation__list-item"><a href="" class="navigation__link"></a></li>
    <li class="navigation__list-item"><a href="" class="navigation__link"></a></li>
  </ul>
</nav>

OK ouais je vois ce que tu veux dire

La réutilisabilité dans BEM

Ce titre, ce sous-titre et ce bouton doivent-ils vraiment être conditionnés par leur block "card" ?

Pourquoi ne pas les rendre réutilisables ?

.card
  .card__image
  .card__body
    .card__title
    .card__subtitle
    .card__button-cta

BEM

.card
  img
  figcaption
    .heading-3
    .text-intro
    .button-primary

pas BEM

Ce titre, ce sous-titre et ce bouton sont réutilisables dans tout le projet.

(oui mais on retombe sur le problème du nommage des éléments 🤯)

(et il peut y avoir conflits de styles entre composants)

pros 👌

cons 🤨

Pas de surprises de nommage

Created by potrace 1.15, written by Peter Selinger 2001-2017

Fichier HTML alourdi et complexifié en raison de (longues) classes sur chaque élément

Created by potrace 1.15, written by Peter Selinger 2001-2017

Écosystème et documentation riches (tutos, plugins navigateurs, etc.)

Created by potrace 1.15, written by Peter Selinger 2001-2017

"Scalable" : excellente évolutivité du projet

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas toujours évident de trancher (Block ou Element ?)

Created by potrace 1.15, written by Peter Selinger 2001-2017

Problème de réutilisabilité des composants

Created by potrace 1.15, written by Peter Selinger 2001-2017

BEM

S'intègre facilement à d'autres outils CSS (Bootstrap, Sass, etc.)

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas de problème de cascade

Created by potrace 1.15, written by Peter Selinger 2001-2017
BEM

la Méthodologie "Atomique"

l'ère des classes utilitaires !

L'organisation atomique, source Laptrinhx

Méthodologie "Atomique"

les classes atomiques dans Bootstrap

https://getbootstrap.com/docs/4.0/utilities/spacing/ 

<div class=“d-block mr-1 p-2 bg-primary text-white”>

Méthodologie "Atomique"

les classes atomiques dans Tailwind

<div class=“block mr-1 rounded bg-white md:bg-hotpink”>

Tu veux tester ?

Méthodologie "Atomique"

les principes essentiels

À chaque classe CSS correspond une seule fonction
(ex. .text-left {text-align: left} ou .mr-2 {margin-right: 2rem} )

1

2

3

Les styles CSS sont dénués de contexte pour être totalement indépendants de la structure HTML

Les sélecteurs CSS composés n’existent pas
(pas de .menu .list, ou de .navigation > a)

<p class=“block mr-1 rounded bg-white md:bg-hotpink”>

Frameworks "atomiques" : Bootstrap (en partie), Tailwind, Tachyons, Windi CSS, ...

pros 👌

cons 🤨

Pas de problème de nommage

Created by potrace 1.15, written by Peter Selinger 2001-2017

Les classes utilitaires gèrent très finement les styles et leurs variations.

Created by potrace 1.15, written by Peter Selinger 2001-2017

Lecture du code HTML rendue fouillie et complexe (ouais, ça pique les yeux !)

Created by potrace 1.15, written by Peter Selinger 2001-2017

Écosystème riche (tutos, bibliothèques de composants, plugins navigateurs, plugins IDE : intellisense, etc.)

Created by potrace 1.15, written by Peter Selinger 2001-2017

L'outil s'adapte à différents contextes (responsive, survol/focus, dark mode, etc.).

Created by potrace 1.15, written by Peter Selinger 2001-2017

Nécessite une rigueur (guidelines) pour ne pas écrire n'importe quoi et dans n'importe quel ordre.

Created by potrace 1.15, written by Peter Selinger 2001-2017

Impossible de trouver des éléments précis dans le code (modale, wrapper, navigation, etc.).

Created by potrace 1.15, written by Peter Selinger 2001-2017

Inadapté pour les layouts "complexes" (gabarits, grilles non classiques, flexbox exotique, etc.).

Created by potrace 1.15, written by Peter Selinger 2001-2017

Inadapté pour pas mal de fonctionnalités (transitions, animations, filtres, transformations) 

Created by potrace 1.15, written by Peter Selinger 2001-2017

Tailwind

Tailwind CSS

Besoin d'une cheatsheet TW, parce que... items-baseline backdrop-invert-0 md:row-start-5 sm:content-around leading-snug dark:tracking-wider

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas besoin d'autres outils CSS (Bootstrap, Sass, etc.)

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas de problème de cascade

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas de code mort

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas de duplication de sélecteurs

Created by potrace 1.15, written by Peter Selinger 2001-2017

Pas besoin de chercher du CSS dans X fichiers

Created by potrace 1.15, written by Peter Selinger 2001-2017

Toutes les valeurs dans un seul fichier de config

Created by potrace 1.15, written by Peter Selinger 2001-2017

utilities

Bon alors au final

on fait quoi de tout ça maintenant ?

sources : https://stackshare.io/ ,  builtwith.com, wappalyzer.com (entre autres)

  • spotify
  • etsy
  • fox news
  • reuters
  • twitter -> 𝕏
  • linkedin
  • snapchat
  • mastercard
  • duolingo
  • alibaba
  • netflix
  • shopify
  • twitch
  • medium
  • starbucks
  • prestashop
  • stackoverflow
  • leafly
  • blizzard
  • github copilot
  • yandex
  • the guardian
  • BBC
  • financial times
  • linkedin
  • shopify
  • duckduckgo
  • uk.gov
  • dropbox
  • jetbrains

Bootstrap

(utility / atomic)

BEM

Tailwind

Nous utilisons TailwindCSS pour tous nos projets en front-end.

source : Guidelines CSS Alsacréations (2021)

🤷 Au bout de deux ans, notre bilan est mitigé

Choix du full-utilities

Concrètement : on a besoin de nommer les choses (ex. .navigation, .modal)

Nous utilisons les classes utilitaires uniquement lorsqu'il s'agit de propriétés liées aux espacements (margin, padding, gap), à la typographie et aux couleurs.

source : Guidelines CSS Alsacréations (2023)

🤷 L'information est éparpillée et on ne trouve pas forcément ce qu'on cherche au bon endroit

Choix du "ça dépend"

Concrètement : display: grid vs gap ou position vs top|right|bottom|left ou border-width vs border-color

On n'utilise pas Tailwind par défaut, sauf si les contraintes du projet, ou le client, l'exigent.

Choix du no-utilities

🤔 Pas de classes utilitaires, mais…

Concrètement : notre outillage interne permet de s'en passer

Reset CSS @import "tailwindcss/preflight.css" layer(base); @import "reset.css" layer(config);
Tailles de polices text-4 md:text-6 lg:text-8 --text-m (variables avec clamp())
Espaces, gouttières mt-2 md:mt-4 --spacing-s (variables avec clamp())
Couleurs (dark mode) bg-hotpink dark:bg-pink --on-surface (variables avec light-dark())
Layouts grid grid-cols-2 gap-4 data-layout="" (layouts HTML)

du design à l'intégration

UI-Kit + bonne webdesigneuse = 💖

UI-Kit indispensable

afficher des specs dans l'UI-Kit

UI-Kit complet

Penser à présenter toutes les variantes dans l'UI-Kit

Des variables dès la conception

ces variables seront transmises au développeurs

Figma vers CSS

Figma

/* ----------------------------------
 * Theme-tokens, généré par primary.alsacreations.com
 * Surcouche de theme.css
 * Configuration :
 * - Couleur primaire : blue
 * - Theme : light et dark
 * - Typographie responsive : oui
 * - Espacements responsive : oui
 * ----------------------------------
 */
:root {
  color-scheme: light dark;

  &[data-theme="light"] {
    color-scheme: light;
  }

  &[data-theme="dark"] {
    color-scheme: dark;
  }

  /* Color Tokens */

  /* Couleur primaire */
  --primary: var(--color-blue-500);
  --on-primary: var(--color-white);

  /* Surface du document */
  --surface: light-dark(var(--color-white), var(--color-gray-900));
  --on-surface: light-dark(var(--color-gray-900), var(--color-white));

  /* Niveaux de profondeur */
  --layer: light-dark(var(--color-white), var(--color-gray-900));

  /* Autres couleurs */
  --surface-dim: light-dark(var(--color-slate-100), var(--color-slate-800));
  --accent-blue: light-dark(var(--color-blue-500), var(--color-blue-400));
  --accent-green: light-dark(var(--color-green-500), var(--color-green-400));
  --accent-pink: light-dark(var(--color-pink-700), var(--color-pink-300));
  --accent-orange: var(--color-orange);
  --on-surface2: light-dark(var(--color-gray-700), var(--color-gray-300));

  /* Typographie — Tailles de police */
  --text-xs: clamp(var(--text-12), 0.7011rem + 0.2174vw, var(--text-14));
  --text-s: clamp(var(--text-14), 0.8261rem + 0.2174vw, var(--text-16));
  --text-m: clamp(var(--text-16), 0.9511rem + 0.2174vw, var(--text-18));
  --text-xl: clamp(var(--text-18), 0.9783rem + 0.6522vw, var(--text-24));
  --text-2xl: clamp(var(--text-24), 1.3533rem + 0.6522vw, var(--text-30));
  --text-4xl: clamp(var(--text-30), 1.4348rem + 1.9565vw, var(--text-48));
  --text-5xl: clamp(var(--text-36), 1.663rem + 2.6087vw, var(--text-60));
  --text-6xl: clamp(var(--text-36), 1.1739rem + 4.7826vw, var(--text-80));

  /* Typographie — Hauteurs de lignes */
  --line-height-s: clamp(var(--line-height-20), 1.1522rem + 0.4348vw, var(--line-height-24));
  --line-height-m: clamp(var(--line-height-24), 1.4022rem + 0.4348vw, var(--line-height-28));
  --line-height-2xl: clamp(var(--line-height-32), 1.9022rem + 0.4348vw, var(--line-height-36));
  --line-height-5xl: clamp(var(--line-height-40), 1.8152rem + 3.0435vw, var(--line-height-68));
  --line-height-4xl: clamp(var(--line-height-40), 2.1087rem + 1.7391vw, var(--line-height-56));
  --line-height-6xl: clamp(var(--line-height-80), 4.5109rem + 2.1739vw, var(--line-height-100));

  /* Espacements */
  --spacing-xs: var(--spacing-4);
  --spacing-s: clamp(var(--spacing-8), 0.3043rem + 0.8696vw, var(--spacing-16));
  --spacing-m: clamp(var(--spacing-16), 0.6087rem + 1.7391vw, var(--spacing-32));
  --spacing-l: clamp(var(--spacing-24), 0.913rem + 2.6087vw, var(--spacing-48));
  --spacing-xl: clamp(var(--spacing-32), 0.8261rem + 5.2174vw, var(--spacing-80));

  /* Formulaires */
  --form-control-background: light-dark(var(--color-gray-200), var(--color-gray-700));
  --on-form-control: light-dark(var(--color-gray-900), var(--color-gray-100));
  --form-control-spacing: var(--spacing-12) var(--spacing-16);
  --form-control-border-width: 1px;
  --form-control-border-color: light-dark(var(--color-gray-400), var(--color-gray-600));
  --form-control-border-radius: var(--radius-none);
  --checkables-border-color: light-dark(var(--color-gray-400), var(--color-gray-600));
  --checkable-size: 1.25em;
}

/* ----------------------------------
 * Theme du projet (valeurs primitives)
 * ----------------------------------
 */

/* stylelint-disable */
/* Breakpoints custom (si compatibles) */
@custom-media --md (width >= 48rem);
@custom-media --lg (width >= 64rem);
@custom-media --xl (width >= 80rem);
@custom-media --xxl (width >= 96rem);
@custom-media --until-md (width < 48rem);
@custom-media --until-lg (width < 64rem);
@custom-media --until-xl (width < 80rem);
@custom-media --until-xxl (width < 96rem);
/* stylelint-enable */

:root {

  /* Breakpoints (en dur) */
  --md: 48rem; /* 768px */
  --lg: 64rem; /* 1024px */
  --xl: 80rem; /* 1280px */
  --xxl: 96rem; /* 1536px */

  /* Transitions et animations */
  --transition-duration: 0.25s;

  /* Niveaux de z-index */
  --z-under-page-level: -1;
  --z-above-page-level: 1;
  --z-header-level: 1000;
  --z-above-header-level: 2000;
  --z-above-all-level: 3000;

  /* Couleurs (globales) */
  --color-white: oklch(1 0 0);
  --color-black: oklch(0 0 0);
  --color-gray-50: oklch(0.97 0 0);
  --color-gray-100: oklch(0.922 0 0);
  --color-gray-200: oklch(0.87 0 0);
  --color-gray-300: oklch(0.708 0 0);
  --color-gray-400: oklch(0.556 0 0);
  --color-gray-500: oklch(0.439 0 0);
  --color-gray-600: oklch(0.371 0 0);
  --color-gray-700: oklch(0.269 0 0);
  --color-gray-800: oklch(0.205 0 0);
  --color-gray-900: oklch(0.145 0 0);
  --color-error-100: oklch(0.97 0.1 27.52);
  --color-error-300: oklch(0.7054 0.19 27.52);
  --color-error-500: oklch(0.5054 0.19 27.52);
  --color-error-700: oklch(0.3554 0.19 27.52);
  --color-error-900: oklch(0.2054 0.11 27.52);
  --color-success-100: oklch(0.9446 0.13 150.685);
  --color-success-300: oklch(0.7166 0.13 150.73);
  --color-success-500: oklch(0.5166 0.13 150.73);
  --color-success-700: oklch(0.3666 0.13 150.73);
  --color-success-900: oklch(0.2166 0.13 150.73);
  --color-warning-100: oklch(0.97 0.08 49.95);
  --color-warning-300: oklch(0.8315 0.17 49.95);
  --color-warning-500: oklch(0.6315 0.17 49.95);
  --color-warning-700: oklch(0.4815 0.17 49.95);
  --color-warning-900: oklch(0.3315 0.11 49.95);
  --color-info-100: oklch(0.97 0.09 256.37);
  --color-info-300: oklch(0.7133 0.18 256.37);
  --color-info-500: oklch(0.5133 0.18 256.37);
  --color-info-700: oklch(0.3633 0.18 256.37);
  --color-info-900: oklch(0.2133 0.11 256.37);

  /* Border radius */
  --radius-none: 0;
  --radius-4: 0.25rem;
  --radius-8: 0.5rem;
  --radius-12: 0.75rem;
  --radius-16: 1rem;
  --radius-24: 1.5rem;
  --radius-full: 9999px;

  /* Espacements */
  --spacing-0: 0;
  --spacing-2: 0.125rem;
  --spacing-4: 0.25rem;
  --spacing-8: 0.5rem;
  --spacing-12: 0.75rem;
  --spacing-16: 1rem;
  --spacing-20: 1.25rem;
  --spacing-24: 1.5rem;
  --spacing-32: 2rem;
  --spacing-40: 2.5rem;
  --spacing-48: 3rem;
  --spacing-56: 3.5rem;
  --spacing-80: 5rem;
  --spacing-128: 8rem;
  --spacing-160: 10rem;

  /* Couleurs du projet */
  --color-color: oklch(1 0 89.8756);
  --color-blue-500: oklch(0.5407 0.1997 260.1494);
  --color-slate-100: oklch(0.9683 0.0069 247.8956);
  --color-slate-800: oklch(0.2795 0.0368 260.031);
  --color-blue-400: oklch(0.6747 0.1725 259.6117);
  --color-green-500: oklch(0.6373 0.1341 123.1529);
  --color-green-400: oklch(0.8211 0.1786 120.0286);
  --color-pink-700: oklch(0.5246 0.199 3.9582);
  --color-pink-300: oklch(0.8228 0.1095 346.0184);
  --color-orange: oklch(0.6248 0.194 35.0652);

  /* Typographie — Tailles de police */
  --text-12: 0.75rem;
  --text-14: 0.875rem;
  --text-16: 1rem;
  --text-18: 1.125rem;
  --text-24: 1.5rem;
  --text-30: 1.875rem;
  --text-36: 2.25rem;
  --text-48: 3rem;
  --text-60: 3.75rem;
  --text-80: 5rem;

  /* Typographie - Familles de police */
  --font-base: system-ui, sans-serif;
  --font-mono: ui-monospace, monospace;

  /* Typographie - Graisses de police */
  --font-weight-regular: 400;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  --font-weight-extrabold: 800;
  --font-weight-black: 900;

  /* Typographie — Hauteurs de lignes */
  --line-height-20: 1.25rem;
  --line-height-24: 1.5rem;
  --line-height-28: 1.75rem;
  --line-height-32: 2rem;
  --line-height-36: 2.25rem;
  --line-height-40: 2.5rem;
  --line-height-56: 3.5rem;
  --line-height-68: 4.25rem;
  --line-height-80: 5rem;
  --line-height-100: 6.25rem;

}

theme.css

theme-tokens.css

primary.alsacreations.com

Figma vers CSS

kiwipedia

ressources et guidelines Alsacréations

OK, la suite ?

et sois en forme !