conventions de nommage, BEM, Tailwind, etc.
Tailwind
2017
BEM
2010
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
"un peu d'organisation, diantre !"
Les problématiques à résoudre
Appliquer une méthodologie (BEM, Atomique)
Du design à l'intégration
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 :
Solutions envisageables :
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 }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 :
Solutions envisageables :
3
.wrapper { }
.wrapper h1, .wrapper h2, .wrapper p { }
.wrapper:hover, .wrapper:focus { }
.wrapper::before { }
@media (width > 640px) {
.wrapper { }
.wrapper::before { }
}Inconvénients majeurs :
Solutions envisageables :
... mais attention au nesting multiple !
.wrapper {
& h1,
& h2,
& p {}
&:hover,
&:focus {}
&::before {}
@media (width > 640px) {
&::before {}
}
}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 :
Solutions envisageables :
.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;
}5
/* on préfère ne rien supprimer */
a.special {color: tomato;}
a.alternate {color: pink;}Inconvénients majeurs :
Solutions envisageables :
<!-- lien à remplacer par un button -->
<a class="alternate special">
Cliquez-moi !
</a>Les Méthodologies
OK, alors on fait quoi ?
Une Convention de nommage à toute épreuve !
Block - Element - Modifier
<p class="author__bio--current">
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
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
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
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-ctaBEM
.card
img
figcaption
.heading-3
.text-intro
.button-primarypas 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)
Pas de surprises de nommage
Fichier HTML alourdi et complexifié en raison de (longues) classes sur chaque élément
Écosystème et documentation riches (tutos, plugins navigateurs, etc.)
"Scalable" : excellente évolutivité du projet
Pas toujours évident de trancher (Block ou Element ?)
Problème de réutilisabilité des composants
S'intègre facilement à d'autres outils CSS (Bootstrap, Sass, etc.)
Pas de problème de cascade
l'ère des classes utilitaires !
L'organisation atomique, source Laptrinhx
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”>
les classes atomiques dans Tailwind
<div class=“block mr-1 rounded bg-white md:bg-hotpink”>
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, ...
Pas de problème de nommage
Les classes utilitaires gèrent très finement les styles et leurs variations.
Lecture du code HTML rendue fouillie et complexe (ouais, ça pique les yeux !)
Écosystème riche (tutos, bibliothèques de composants, plugins navigateurs, plugins IDE : intellisense, etc.)
L'outil s'adapte à différents contextes (responsive, survol/focus, dark mode, etc.).
Nécessite une rigueur (guidelines) pour ne pas écrire n'importe quoi et dans n'importe quel ordre.
Impossible de trouver des éléments précis dans le code (modale, wrapper, navigation, etc.).
Inadapté pour les layouts "complexes" (gabarits, grilles non classiques, flexbox exotique, etc.).
Inadapté pour pas mal de fonctionnalités (transitions, animations, filtres, transformations)
Besoin d'une cheatsheet TW, parce que... items-baseline backdrop-invert-0 md:row-start-5 sm:content-around leading-snug dark:tracking-wider
Pas besoin d'autres outils CSS (Bootstrap, Sass, etc.)
Pas de problème de cascade
Pas de code mort
Pas de duplication de sélecteurs
Pas besoin de chercher du CSS dans X fichiers
Toutes les valeurs dans un seul fichier de config
utilities
on fait quoi de tout ça maintenant ?
sources : https://stackshare.io/ , builtwith.com, wappalyzer.com (entre autres)
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.
source : Guidelines CSS Alsacréations (2025)
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) |
UI-Kit + bonne webdesigneuse = 💖
afficher des specs dans l'UI-Kit
Penser à présenter toutes les variantes dans l'UI-Kit
ces variables seront transmises au développeurs
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
ressources et guidelines Alsacréations
et sois en forme !