Titre de la carte
Description du contenu avec les éléments BEM bien organisés.
Organisez votre CSS comme un pro : BEM, SMACSS, ITCSS, OOCSS, CUBE CSS, Atomic CSS… Découvrez les méthodologies qui transforment un fouillis de styles en un système maintenable et scalable.
Sans architecture, un projet CSS devient rapidement un cauchemar de maintenabilité. Les sélecteurs se contournent, les spécificités explosent, les classes sont nommées de façon aléatoire, et chaque modification risque de casser quelque chose d'autre.
| Sans architecture | Avec architecture |
|---|---|
Classes nommées au hasard (.red, .big, .box2) |
Classes sémantiques et prévisibles (.card__title, .btn--primary) |
Sélecteurs ultra-spcifiques (#main > div:nth-child(3) .title) |
Sélecteurs plats et modulaires (.card__title) |
| Duplication massive de styles | Composants réutilisables, DRY |
| Fichier CSS monolithique de 5000 lignes | Fichiers modulaires et ciblés |
| Peur de toucher au CSS | Confiance dans les modifications |
| Conflits entre développeurs | Conventions claires, travail en équipe fluide |
/* Structure : bloc__element--modificateur */
/* BLOC — composant autonome */
.card { }
/* ÉLÉMENT — partie du bloc (double underscore __) */
.card__header { }
.card__body { }
.card__footer { }
.card__title { }
.card__image { }
/* MODIFICATEUR — variante ou état (double tiret --) */
.card--featured { }
.card--dark { }
.card--compact { }
/* ÉLÉMENT + MODIFICATEUR */
.card__title--large { }
.card__button--disabled { }
.navigation
.form
.header
.sidebar
.modal
.navigation__item
.form__input
.header__logo
.sidebar__menu
.modal__title
.navigation__item--active
.form__input--error
.header--sticky
.sidebar--collapsed
.modal--open
/* ❌ Trop de profondeur — max 3 niveaux */
.page__content__card__header__title { }
/* ✅ Suffisant */
.card__title { }
/* ❌ Modificateur sans bloc */
.--active { }
__title { }
/* ✅ Toujours avec le bloc parent */
.card--active { }
.card__title { }
/* ❌ Élément qui existe seul */
.user { } /* pas de .user__ ? Ce n'est pas un élément */
.user__avatar { } /* OK — avatar est un élément de user */
.bloc__el__el--mod max).card__title, pas .card__big-text)- (kebab-case)<article class="card card--featured">
<div class="card__image">
<img src="photo.jpg" alt="...">
<span class="card__badge card__badge--new">
Nouveau
</span>
</div>
<div class="card__content">
<h3 class="card__title">Titre</h3>
<p class="card__text">Description</p>
<div class="card__meta">
<span class="card__date">12 mars</span>
<span class="card__author">Jean</span>
</div>
</div>
<div class="card__footer">
<button class="card__btn card__btn--primary">
Lire
</button>
<button class="card__btn card__btn--ghost">
Partager
</button>
</div>
</article>
/* BLOC */
.card {
border: 2px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
background: white;
}
/* MODIFICATEUR de bloc */
.card--featured {
border-color: #2563eb;
}
/* ÉLÉMENTS */
.card__image {
position: relative;
height: 200px;
background: linear-gradient(135deg, #2563eb, #7c3aed);
}
.card__content { padding: 1.5rem; }
.card__title { margin: 0 0 0.5rem; font-size: 1.25rem; }
.card__text { color: #64748b; margin: 0 0 1rem; line-height: 1.6; }
.card__meta { display: flex; gap: 0.5rem; font-size: 0.8rem; color: #94a3b8; }
.card__footer {
padding: 1rem 1.5rem;
border-top: 1px solid #e2e8f0;
display: flex;
gap: 0.5rem;
}
/* ÉLÉMENTS + MODIFICATEURS */
.card__badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
}
.card__badge--new { background: #2563eb; color: white; }
.card__btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.card__btn--primary { background: #2563eb; color: white; }
.card__btn--ghost { background: transparent; color: #2563eb; }
/* ============================================
SMACSS — Les 5 catégories
============================================ */
/* 1. BASE — Reset et styles par défaut des éléments */
body { font-family: sans-serif; }
h1, h2, h3 { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
/* 2. LAYOUT — Structure de la page (préfixe: l-) */
.l-header { }
.l-sidebar { }
.l-content { }
.l-footer { }
.l-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
}
/* 3. MODULE — Composants réutilisables (préfixe: .) */
.card { }
.card__title { }
.card__image { }
.btn { }
.btn--primary { }
.nav { }
.nav__item { }
/* 4. STATE — États dynamiques (préfixe: is-) */
.is-active { }
.is-hidden { }
.is-open { }
.is-disabled { }
.is-loading { }
/* 5. THEME — Variations visuelles (préfixe: theme-) */
.theme-dark { }
.theme-light { }
.theme-high-contrast { }
SMACSS — hiérarchie des catégories
═══════════════════════════════════
BASE (éléments HTML)
└── styles par défaut, reset
└── pas de classe, pas de IDs
└── body, h1, a, img, table...
LAYOUT (page structure)
└── préfixe: l-
└── .l-header, .l-sidebar, .l-content
└── grid, flexbox, positionnement
MODULE (composants)
└── pas de préfixe spécial
└── .card, .btn, .nav, .form
└── parties réutilisables
STATE (états dynamiques)
└── préfixe: is-
└── .is-active, .is-hidden
└── modifié par JS ou media queries
THEME (variations visuelles)
└── préfixe: theme-
└── .theme-dark, .theme-light
└── couleurs, polices spécifiques
Les 5 catégories SMACSS et leur hiérarchie
/* ============================================
ITCSS — Imports dans l'ordre
============================================ */
/* 1. Settings — Tokens de design */
@import 'settings/variables';
@import 'settings/colors';
@import 'settings/typography';
/* 2. Tools — Fonctions et mixins */
@import 'tools/functions';
@import 'tools/mixins';
/* 3. Generic — Reset global */
@import 'generic/reset';
@import 'generic/normalize';
/* 4. Elements — Styles nus */
@import 'elements/headings';
@import 'elements/links';
@import 'elements/lists';
/* 5. Objects — Layout structures */
@import 'objects/container';
@import 'objects/grid';
@import 'objects/wrapper';
/* 6. Components — Composants UI */
@import 'components/header';
@import 'components/card';
@import 'components/button';
@import 'components/form';
@import 'components/modal';
/* 7. Utilities — Overrides */
@import 'utilities/visibility';
@import 'utilities/spacing';
@import 'utilities/text';
/* ============================================
OOCSS — Séparation structure / peau
============================================ */
/* STRUCTURE — la forme (objets réutilisables) */
.media {
display: flex;
align-items: flex-start;
gap: 1rem;
}
.media__body { flex: 1; }
/* PEAU — l'apparence (skins combinables) */
.media--light {
background: #f8fafc;
border: 1px solid #e2e8f0;
}
.media--dark {
background: #1e293b;
color: white;
}
.media--primary {
border-left: 4px solid #2563eb;
}
/* UTILISATION — combinaison structure + peau */
/* <div class="media media--light media--primary"> */
/* Autre exemple avec les boutons */
/* Structure */
.btn { display: inline-flex; align-items: center; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; }
.btn--large { padding: 0.75rem 1.5rem; }
.btn--small { padding: 0.25rem 0.5rem; }
/* Peau */
.btn--primary { background: #2563eb; color: white; }
.btn--secondary { background: #64748b; color: white; }
.btn--danger { background: #dc2626; color: white; }
/* Combinaison : btn + btn--large + btn--primary */
/* ============================================
CUBE CSS — Les 4 concepts
============================================ */
/* COMPOSITION — Layout global, structures de page */
/* Pas de style visuel, juste de la structure */
.composition {
display: grid;
gap: var(--gap, 1rem);
padding: var(--padding, 1rem);
}
/* UTILITY — Helpers utilitaires, overrides */
.u-text-center { text-align: center; }
.u-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.u-bg-primary { background: #2563eb; }
.u-mt-4 { margin-top: 1rem; }
/* BLOCK — Composants, entités de contenu */
.card {
border: 1px solid var(--border);
border-radius: var(--radius, 8px);
padding: var(--space, 1rem);
}
.card__title { font-size: 1.25rem; font-weight: 700; }
.card__body { color: var(--text-secondary); }
/* EXCEPTION — Overrides contextuels */
.card--featured {
/* Exception au bloc .card */
border-color: var(--color-primary);
box-shadow: var(--shadow-lg);
}
/* Le bloc gère son propre padding, margin, background... */
/* La composition gère le layout */
/* Les utilities sont des raccourcis ponctuels */
/* Les exceptions modifient un bloc dans un contexte spécifique */
<!-- Approche classique (composants) -->
<button class="btn btn-primary">Envoyer</button>
<!-- Approche Atomic / Utility-first (Tailwind) -->
<button class="
inline-flex items-center justify-center
px-4 py-2
bg-blue-600 text-white font-semibold
rounded-lg
hover:bg-blue-700
focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
transition-colors
disabled:opacity-50 disabled:cursor-not-allowed
">Envoyer</button>
<!-- Chaque classe = 1 propriété CSS -->
<!-- px-4 → padding-left: 1rem; padding-right: 1rem; -->
<!-- py-2 → padding-top: 0.5rem; padding-bottom: 0.5rem; -->
<!-- bg-blue-600 → background-color: #2563eb; -->
<!-- rounded-lg → border-radius: 0.5rem; -->
<!-- hover:bg-blue-700 → @media(hover:hover) { &:hover { ... } } -->
| Critère | BEM | SMACSS | ITCSS | OOCSS | CUBE CSS | Atomic |
|---|---|---|---|---|---|---|
| Type | Nommage | Catégorisation | Architecture | Principes | Cadre | Nommage |
| Complexité | ⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐ |
| Spécificité | ✓ Contrôlée | ✓ Contrôlée | ✓ Contrôlée | ~ Variable | ✓ Contrôlée | ✓ Très basse |
| DRY | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| Courbe d'apprentissage | Facile | Moyen | Élevée | Moyen | Moyen | Facile |
| Taille du CSS généré | Moyen | Moyen | Moyen | Optimisé | Moyen | Grand (sans purge) |
| Popularité | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| Ideal pour | Tout projet | Projets moyens | Gros projets | Design systems | Projets modernes | Prototypage rapide |
╔══════════════════════════════════════════════════════════════════╗ ║ ITCSS — TRIANGLE INVERSÉ ║ ╠══════════════════════════════════════════════════════════════════╣ ║ ║ ║ SPECIFICITÉ │ COUCHE ║ ║ CROISSANTE │ ║ ║ │ ║ ║ ─────────────── │ ──────────────────────────────────── ║ ║ Faible │ 7. Utilities ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ║ ║ │ 6. Components ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ║ ║ │ 5. Objects ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ║ ║ │ 4. Elements ▓▓▓▓▓▓▓▓▓▓▓ ║ ║ │ 3. Generic ▓▓▓▓▓▓▓▓ ║ ║ │ 2. Tools ▓▓▓▓▓ ║ ║ Forte │ 1. Settings ▓▓▓ ║ ║ │ ║ ║ ════════════════════════════════════════════════════════════ ║ ║ Portée │ Globale ◄────────────► Spécifique ║ ║ Quantité │ Petite ◄─────────────► Grande ║ ║ Risque conflit │ Faible ◄────────────► Élevé ║ ╚══════════════════════════════════════════════════════════════════╝
Le triangle inversé d'ITCSS : les couches les plus spécifiques sont en bas
/* ============================================
main.css — Point d'entrée unique
============================================ */
@import 'settings/variables.css';
@import 'generic/reset.css';
@import 'layout/grid.css';
@import 'components/card.css';
@import 'components/button.css';
@import 'components/nav.css';
@import 'utilities/helpers.css';
/* ============================================
settings/variables.css — Design tokens
============================================ */
:root {
/* Couleurs */
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-secondary: #7c3aed;
--color-success: #16a34a;
--color-warning: #f59e0b;
--color-error: #dc2626;
--color-text: #1e293b;
--color-text-secondary: #64748b;
--color-bg: #ffffff;
--color-bg-secondary: #f8fafc;
--color-border: #e2e8f0;
/* Espacement */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
/* Typographie */
--font-sans: 'Segoe UI', system-ui, sans-serif;
--font-mono: 'Cascadia Code', monospace;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
/* Ombres */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
/* Bordures */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Transitions */
--transition-fast: 0.15s ease;
--transition-base: 0.3s ease;
}
/* ============================================
generic/reset.css — Reset minimal
============================================ */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-sans);
font-size: var(--text-base);
color: var(--color-text);
line-height: 1.6;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
button { cursor: pointer; font: inherit; }
/* ============================================
layout/grid.css — Grille responsive
============================================ */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--space-4);
}
.grid {
display: grid;
gap: var(--space-6);
}
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 768px) {
.grid--2, .grid--3 { grid-template-columns: 1fr; }
}
/* ============================================
components/card.css — Composant carte (BEM)
============================================ */
.card {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: box-shadow var(--transition-base);
}
.card:hover {
box-shadow: var(--shadow-md);
}
.card__image {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
}
.card__content {
padding: var(--space-6);
}
.card__title {
font-size: var(--text-xl);
font-weight: 700;
margin-bottom: var(--space-2);
}
.card__text {
color: var(--color-text-secondary);
margin-bottom: var(--space-4);
}
.card--featured {
border-color: var(--color-primary);
}
.card--dark {
background: #1e293b;
border-color: #334155;
color: #f1f5f9;
}
/* ============================================
components/button.css — Composant bouton (BEM)
============================================ */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-6);
border: 2px solid transparent;
border-radius: var(--radius-md);
font-weight: 600;
font-size: var(--text-base);
cursor: pointer;
transition: all var(--transition-fast);
}
.btn--primary {
background: var(--color-primary);
color: white;
}
.btn--primary:hover {
background: var(--color-primary-hover);
}
.btn--outline {
background: transparent;
color: var(--color-primary);
border-color: var(--color-primary);
}
.btn--outline:hover {
background: var(--color-primary);
color: white;
}
.btn--ghost {
background: transparent;
color: var(--color-text-secondary);
}
.btn--ghost:hover {
background: var(--color-bg-secondary);
color: var(--color-text);
}
.btn--small { padding: var(--space-2) var(--space-4); font-size: var(--text-sm); }
.btn--large { padding: var(--space-4) var(--space-8); font-size: var(--text-lg); }
/* ============================================
utilities/helpers.css — Classes utilitaires
============================================ */
.u-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.u-text-center { text-align: center; }
.u-text-right { text-align: right; }
.u-mt-4 { margin-top: var(--space-4); }
.u-mb-4 { margin-bottom: var(--space-4); }
.u-hidden { display: none; }
.u-flex { display: flex; }
.u-flex-center { display: flex; align-items: center; justify-content: center; }
/* ============================================
SMACSS — Layout de page type blog
============================================ */
/* BASE */
body { font-family: system-ui, sans-serif; color: #1e293b; }
h1 { font-size: 2rem; margin-bottom: 1rem; }
p { margin-bottom: 1rem; line-height: 1.7; }
/* LAYOUT */
.l-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: white;
border-bottom: 1px solid #e2e8f0;
}
.l-main {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.l-sidebar {
padding: 2rem;
background: #f8fafc;
}
/* MODULE */
.post { margin-bottom: 2rem; padding: 1.5rem; border: 1px solid #e2e8f0; border-radius: 8px; }
.post__title { font-size: 1.5rem; margin-bottom: 0.5rem; }
.post__date { font-size: 0.8rem; color: #94a3b8; }
.post__excerpt { color: #475569; }
.tag { display: inline-block; padding: 0.2rem 0.6rem; border-radius: 4px; font-size: 0.75rem; background: #eff6ff; color: #2563eb; }
.tag--hot { background: #fef2f2; color: #dc2626; }
/* STATE */
.is-featured { border-left: 4px solid #2563eb; }
.is-hidden { display: none; }
.is-loading { opacity: 0.5; pointer-events: none; }
<!-- Layout complet sans écrire une ligne de CSS personnalisé -->
<div class="container mx-auto px-4 max-w-6xl">
<header class="flex items-center justify-between py-4 mb-8">
<h1 class="text-2xl font-bold text-gray-900">Mon Site</h1>
<nav class="flex gap-6">
<a href="#" class="text-gray-600 hover:text-blue-600 transition-colors">Accueil</a>
<a href="#" class="text-gray-600 hover:text-blue-600 transition-colors">Blog</a>
<a href="#" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors">Connexion</a>
</nav>
</header>
<main class="grid grid-cols-1 md:grid-cols-3 gap-8">
<article class="md:col-span-2">
<div class="bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm hover:shadow-md transition-shadow">
<img src="photo.jpg" class="w-full h-48 object-cover" alt="...">
<div class="p-6">
<h2 class="text-xl font-bold mb-2">Titre de l'article</h2>
<p class="text-gray-500 mb-4">Extrait de l'article...</p>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-semibold hover:bg-blue-700">Lire la suite</button>
</div>
</div>
</article>
<aside class="space-y-6">
<div class="bg-gray-50 rounded-xl p-6">
<h3 class="font-bold mb-3">À propos</h3>
<p class="text-sm text-gray-500">Description du blog...</p>
</div>
</aside>
</main>
</div>
/* ============================================
CUBE CSS — Composant Card
============================================ */
/* COMPOSITION — Le conteneur */
.card-composition {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
/* BLOCK — Le composant */
.cube-card {
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
background: var(--color-bg);
}
.cube-card__img {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
}
.cube-card__body {
padding: var(--space-6);
}
.cube-card__title {
font-size: var(--text-xl);
font-weight: 700;
margin-bottom: var(--space-2);
}
.cube-card__text {
color: var(--color-text-secondary);
line-height: 1.6;
}
/* UTILITY — Overrides ponctuels */
.u-card-highlight { border-color: var(--color-primary); }
/* EXCEPTION — Contexte spécifique */
.sidebar .cube-card {
flex-direction: row;
}
.sidebar .cube-card__img {
width: 120px;
aspect-ratio: 1;
}
/* FAUX — le nom dépend du style visuel */
.red-text { color: red; }
.big-box { font-size: 2rem; }
.rounded { border-radius: 50%; }
.shadow { box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
/* CORRECT — nommer selon la fonction/sémantique */
.error-message { color: var(--color-error); }
.heading-primary { font-size: 2rem; }
.avatar { border-radius: 50%; }
.card { box-shadow: var(--shadow-sm); }
/* FAUX — spécificité excessive, impossible à surcharger */
#main > div > section > article > h2.title { }
.page .content .sidebar .widget .list .item .link { }
/* CORRECT — sélecteurs plats et modulaires (BEM) */
.card__title { }
.sidebar__link { }
/* FAUX — !important casse la cascade */
.title { font-size: 2rem !important; }
.text { color: blue !important; padding: 1rem !important; }
/* CORRECT — résoudre le problème de spécificité */
.card__title { font-size: 2rem; }
/* Si un !important est vraiment nécessaire, le limiter aux utilities */
.u-text-center { text-align: center !important; }
/* INCOHÉRENT — mélange de conventions dans un même projet */
.card { } /* BEM */
.card__title { } /* BEM */
.post-content { } /* Pas BEM */
.btn-success { } /* SMACSS ou BEM ? */
.is-active { } /* SMACSS */
.u-mt-4 { } /* Utility */
/* CORRECT — choisir UNE méthodologie et la suivre partout */
.card { } /* BEM */
.card__title { } /* BEM */
.card__text { } /* BEM */
.card--featured { } /* BEM */
.btn { } /* BEM */
.btn--primary { } /* BEM */
/* FAUX — un seul fichier de 3000 lignes */
/* styles.css : 3000 lignes de CSS non organisé */
/* CORRECT — fichiers modulaires importés via main.css */
/* main.css imports :
@import 'settings/variables';
@import 'generic/reset';
@import 'components/card';
@import 'components/button';
@import 'components/nav';
@import 'utilities/helpers';
*/
/* FAUX — trop d'imbrication d'éléments BEM */
.card { }
.card__header { }
.card__header__title { } /* ❌ Imbrication interdite */
.card__header__title__icon { } /* ❌ Trois niveaux max */
/* CORRECT — un seul niveau d'élément */
.card { }
.card__header { }
.card__title { } /* Titre de la carte, pas du header */
.card__icon { } /* Icône de la carte */
/* FAUX — même style copié-collé 5 fois */
.page-header { background: #2563eb; color: white; padding: 2rem; border-radius: 8px; }
.section-header { background: #2563eb; color: white; padding: 2rem; border-radius: 8px; }
.card-header { background: #2563eb; color: white; padding: 2rem; border-radius: 8px; }
/* CORRECT — un composant réutilisable */
.section-header {
background: var(--color-primary);
color: white;
padding: var(--space-8);
border-radius: var(--radius-md);
}
/* Réutiliser .section-header partout */
| Contexte | Méthodologie recommandée | Pourquoi |
|---|---|---|
| Petit site vitrine | BEM simple | Facile à apprendre, suffisant pour la taille |
| Application web moyenne | BEM + fichiers modulaires | Organisation claire, maintenable |
| Gros projet / Design system | ITCSS + BEM | Séparation des couches, scalabilité |
| Prototype rapide | Utility-first (Tailwind) | Vitesse de développement maximale |
| Design system partagé | OOCSS + BEM | Séparation structure/peau, réutilisabilité |
| Projet maintenu par une grande équipe | ITCSS + CUBE CSS | Règles claires, gestion de la cascade |
Défi 1 : Convertir en BEM
Transformez ce CSS non-BEM en architecture BEM propre :
/* AVANT — Non-BEM */
.sidebar .menu .item a.active { color: #2563eb; font-weight: bold; }
.modal .content .close-btn { position: absolute; top: 1rem; right: 1rem; }
.form .field .error-msg { color: #dc2626; font-size: 0.85rem; }
/* APRÈS — BEM */
.nav__link--active { color: #2563eb; font-weight: bold; }
.modal__close { position: absolute; top: 1rem; right: 1rem; }
.form__error { color: #dc2626; font-size: 0.85rem; }
Défi 2 : Créer un design token system
Créez un fichier tokens.css avec au minimum 15 variables couvrant : couleurs, espacement, typographie, ombres et bordures.
/* tokens.css */
:root {
/* 5 couleurs minimum */
--color-primary: #2563eb;
--color-secondary: #7c3aed;
--color-success: #16a34a;
--color-warning: #f59e0b;
--color-error: #dc2626;
/* 5 espacements minimum */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
/* 3 tailles de texte */
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.25rem;
/* 2 ombres */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.1);
/* 2 rayons */
--radius-md: 8px;
--radius-lg: 12px;
}
Défi 3 : Composant BEM complet
Créez un composant "produit" en BEM avec : image, badge, titre, prix, bouton d'ajout au panier, et un modificateur "épuisé".
<!-- HTML attendu -->
<div class="product product--sold-out">
<div class="product__image-wrapper">
<img class="product__image" src="..." alt="...">
<span class="product__badge product__badge--sale">-30%</span>
</div>
<h3 class="product__title">Nom du produit</h3>
<div class="product__pricing">
<span class="product__price">29,99 €</span>
<span class="product__price product__price--old">42,99 €</span>
</div>
<button class="product__btn product__btn--add">Ajouter au panier</button>
</div>
Défi 4 : Organiser les fichiers ITCSS
Organisez les fichiers CSS d'un projet de blog en 7 couches ITCSS. Listez chaque fichier et son contenu typique.
/* Structure attendue :
css/
01-settings/_variables.css → Couleurs, espacements, tokens
02-tools/_mixins.css → @mixin responsive, @function
03-generic/_reset.css → *, box-sizing, body
04-elements/_headings.css → h1-h6, a, p
05-objects/_container.css → .container, .wrapper, .grid
06-components/_card.css → .card, .card__*
06-components/_nav.css → .nav, .nav__*
06-components/_post.css → .post, .post__*
07-utilities/_helpers.css → .u-hidden, .u-text-center
main.css → imports dans l'ordre ITCSS
*/
Défi 5 : SMACSS — Layout de dashboard
Créez un layout de dashboard avec les 5 catégories SMACSS : Base (reset), Layout (grid), Module (cartes), State (loading, active), Theme (dark).
/* SMACSS Layout Dashboard */
/* BASE */
body { font-family: system-ui, sans-serif; }
/* LAYOUT */
.l-dashboard { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
.l-dashboard__sidebar { background: #1e293b; color: white; }
.l-dashboard__main { padding: 2rem; background: #f8fafc; }
/* MODULE */
.stat-card { background: white; border-radius: 12px; padding: 1.5rem; }
.stat-card__label { font-size: 0.85rem; color: #64748b; }
.stat-card__value { font-size: 2rem; font-weight: 700; }
/* STATE */
.is-loading { opacity: 0.5; pointer-events: none; }
.is-active { background: rgba(255,255,255,0.1); }
/* THEME */
.theme-dark .l-dashboard__main { background: #0f172a; }
.theme-dark .stat-card { background: #1e293b; color: white; }
Projet 1 : Design system BEM complet
Créez un design system avec les composants suivants en BEM :
Projet 2 : Blog ITCSS
Créez un blog complet en suivant l'architecture ITCSS :
01-settings03-generic05-objects06-components07-utilitiesProjet 3 : Comparaison de méthodologies
Créez le même composant (une carte produit) avec 4 approches différentes :
| Méthodologie | Concept clé | Syntaxe / Structure |
|---|---|---|
| BEM | Nommage : Bloc__Élément--Modificateur | .card__title--large |
| SMACSS | 5 catégories : Base, Layout, Module, State, Theme | .l-header, .is-active |
| ITCSS | 7 couches ordonnées par portée | Settings → Tools → Generic → Elements → Objects → Components → Utilities |
| OOCSS | Séparation structure / peau | .media + .media--dark |
| CUBE CSS | Composition, Utility, Block, Exception | Layout → Helpers → Composants → Overrides |
| Atomic | 1 classe = 1 propriété CSS | .bg-blue-600 .px-4 .rounded-lg |
__), Modificateur (variante avec --)..is-active (préfixe is-)./* ============================================
BEM — Nommage
============================================ */
.card { }
.card__header { }
.card__title { }
.card__image { }
.card--featured { }
.card--dark { }
.card__title--large { }
/* ============================================
SMACSS — Catégories
============================================ */
/* Base */ body { }
/* Layout */ .l-header { }
/* Module */ .card { }
/* State */ .is-active { }
/* Theme */ .theme-dark { }
/* ============================================
ITCSS — Imports (ordre obligatoire)
============================================ */
@import '01-settings/variables';
@import '02-tools/mixins';
@import '03-generic/reset';
@import '04-elements/headings';
@import '05-objects/container';
@import '06-components/card';
@import '07-utilities/helpers';
/* ============================================
OOCSS — Structure + Peau
============================================ */
/* Structure */ .media { display: flex; }
/* Peau */ .media--dark { background: #1e293b; }
/* ============================================
Règles d'or
============================================ */
/* 1. Max 2 niveaux de sélecteurs */
/* 2. BEM pour le nommage */
/* 3. Variables CSS pour les tokens */
/* 4. Fichiers par composant */
/* 5. !important = utilities seulement */
/* 6. Nommer selon la sémantique */