Bibliothèque complète de composants UI en CSS pur : boutons, cartes, navigation, feedback, overlays, formulaires et layouts. Chaque composant est déclinable, accessible et modulaire.
Les composants UI sont les briques fondamentales d'une interface. Chaque composant possède :
ANATOMIE D'UN COMPOSANT UI
═══════════════════════════
Props (attributs) États
────────────────── ──────────────
• variant (primary...) • :hover
• size (sm, md, lg) • :focus
• disabled • :active
• loading • .is-loading
• icon • .is-disabled
┌─────────────────────────────────────┐
│ ┌──────────┐ ┌────────────────┐ │
│ │ Icone │ │ Texte label │ │ ← Structure HTML
│ └──────────┘ └────────────────┘ │
└─────────────────────────────────────┘
│ │
└───────┬───────┘
│
┌─────────────────────────────────────┐
│ padding, border-radius, shadow │ ← Style CSS
│ colors, transitions, focus ring │
└─────────────────────────────────────┘
Structure typique d'un composant UI
/* Structure BEM pour chaque composant */
.nom-composant { } /* Bloc */
.nom-composant__element { } /* Élément */
.nom-composant--modificateur { } /* Modificateur */
.nom-composant__element--modificateur { } /* Élément modifié */
/* Exemple avec un bouton */
.btn { }
.btn__icon { }
.btn--primary { }
.btn--small { }
.btn--loading { }
.btn__icon--left { }
/* Exemple avec une carte */
.card { }
.card__image { }
.card__title { }
.card__body { }
.card--featured { }
.card--compact { }
CATÉGORIES DE COMPOSANTS ═══════════════════════════ 🔘 BOUTONS 7 variantes × 3 tailles 🃏 CARTES 7 types (basic, product, profile, stats, blog, team, testimonial) 🧭 NAVIGATION 6 composants (navbar, sidebar, breadcrumbs, pagination, tabs, mega-menu) 💬 FEEDBACK 5 composants (alert, toast, badge, progress, skeleton) 🪟 OVERLAY 5 composants (modal, tooltip, popover, dropdown, accordion) 📝 FORMULAIRES 6 composants (input-group, checkbox, radio, switch, select, range, file-upload) 📐 LAYOUT 3 composants (hero, footer, container) Total : 39+ composants
Vue d'ensemble des composants couverts dans ce chapitre
<!-- HTML Boutons -->
<button class="btn btn--primary btn--md">Primaire</button>
<button class="btn btn--secondary btn--md">Secondaire</button>
<button class="btn btn--outline btn--md">Outline</button>
<button class="btn btn--ghost btn--md">Ghost</button>
<button class="btn btn--primary btn--icon">★</button>
<button class="btn btn--primary btn--loading">Chargement</button>
<button class="btn btn--primary btn--disabled">Désactivé</button>
<!-- Tailles -->
<button class="btn btn--primary btn--sm">Small</button>
<button class="btn btn--primary btn--md">Medium</button>
<button class="btn btn--primary btn--lg">Large</button>
/* CSS Boutons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-weight: 600;
border: 2px solid transparent;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
/* Variantes */
.btn--primary { background: var(--color-primary); color: white; }
.btn--primary:hover { background: var(--color-primary-hover); }
.btn--secondary { background: #64748b; color: white; }
.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: #64748b; }
.btn--ghost:hover { background: #f1f5f9; }
/* Tailles */
.btn--sm { padding: 0.4rem 0.8rem; font-size: 0.8rem; }
.btn--md { padding: 0.55rem 1.1rem; font-size: 0.9rem; }
.btn--lg { padding: 0.75rem 1.5rem; font-size: 1rem; }
/* États */
.btn--loading { color: transparent !important; pointer-events: none; position: relative; }
.btn--loading::after {
content: '';
position: absolute;
width: 1rem;
height: 1rem;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
.btn--disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
/* Icône */
.btn--icon { width: 2.2rem; height: 2.2rem; padding: 0; border-radius: 50%; }
<!-- Navbar -->
<nav class="navbar">
<a href="#" class="navbar__logo">MonSite</a>
<ul class="navbar__links">
<li><a href="#" class="navbar__link navbar__link--active">Accueil</a></li>
<li><a href="#" class="navbar__link">Produits</a></li>
</ul>
<a href="#" class="navbar__cta">Connexion</a>
</nav>
Le contenu principal s'affiche ici à côté de la sidebar.
<!-- Tabs -->
<div class="tabs">
<button class="tab tab--active">Onglet 1</button>
<button class="tab">Onglet 2</button>
<button class="tab">Onglet 3</button>
</div>
/* CSS Tabs */
.tabs { display: flex; border-bottom: 2px solid #e2e8f0; }
.tab {
padding: 0.6rem 1.25rem;
border: none;
background: transparent;
color: #64748b;
font-weight: 500;
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: all 0.2s;
}
.tab:hover { color: #1e293b; }
.tab--active { color: #2563eb; border-bottom-color: #2563eb; font-weight: 600; }
<!-- Alertes -->
<div class="alert alert--success">✅ Opération réussie</div>
<div class="alert alert--warning">⚠️ Attention...</div>
<div class="alert alert--error">❌ Erreur...</div>
<div class="alert alert--info">ℹ️ Information</div>
<!-- Badges -->
<span class="badge badge--success">En ligne</span>
<span class="badge badge--warning">En attente</span>
<span class="badge badge--error">Hors ligne</span>
<!-- Badge compteur -->
<span class="badge badge--count">3</span>
<!-- Progress bar -->
<div class="progress">
<div class="progress__bar progress__bar--blue" style="width: 72%;"></div>
</div>
<!-- Modal -->
<div class="modal-overlay">
<div class="modal">
<div class="modal__header">
<h3 class="modal__title">Titre</h3>
<button class="modal__close">✕</button>
</div>
<div class="modal__body">Contenu...</div>
<div class="modal__footer">
<button class="btn btn--ghost">Annuler</button>
<button class="btn btn--primary">Confirmer</button>
</div>
</div>
</div>
<!-- Tooltip -->
<div class="tooltip-wrapper">
<button class="btn btn--primary">Hover</button>
<div class="tooltip">Texte du tooltip</div>
</div>
/* CSS Tooltip */
.tooltip-wrapper { position: relative; display: inline-block; }
.tooltip-wrapper:hover .tooltip { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.tooltip {
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%) translateY(4px);
background: #1e293b;
color: white;
padding: 0.4rem 0.75rem;
border-radius: 6px;
font-size: 0.8rem;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.2s;
}
.tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: #1e293b;
}
<!-- Dropdown -->
<div class="dropdown">
<button class="btn btn--outline dropdown__trigger">Options ▾</button>
<div class="dropdown__menu">
<a href="#" class="dropdown__item">Modifier</a>
<a href="#" class="dropdown__item">Supprimer</a>
</div>
</div>
<!-- Accordion -->
<div class="accordion">
<div class="accordion__item accordion__item--open">
<button class="accordion__header">
<span>Question</span>
<span class="accordion__icon">▼</span>
</button>
<div class="accordion__body">Réponse...</div>
</div>
</div>
<!-- Input group -->
<div class="input-group">
<span class="input-group__prefix">🔍</span>
<input type="text" class="input-group__input" placeholder="Rechercher...">
</div>
<div class="input-group">
<input type="email" class="input-group__input" placeholder="Email">
<span class="input-group__suffix">@gmail.com</span>
</div>
<!-- Checkbox -->
<label class="checkbox">
<input type="checkbox">
<span class="checkbox__check">✓</span>
<span>Label</span>
</label>
<!-- Radio -->
<label class="radio">
<input type="radio" name="group">
<span class="radio__circle"></span>
<span>Option</span>
</label>
<!-- Switch -->
<label class="switch">
<input type="checkbox">
<span class="switch__track">
<span class="switch__thumb"></span>
</span>
<span>Label</span>
</label>
<!-- Hero -->
<section class="hero">
<h1 class="hero__title">Titre principal</h1>
<p class="hero__text">Sous-titre ou description</p>
<div class="hero__actions">
<button class="btn btn--primary btn--lg">CTA</button>
<button class="btn btn--ghost btn--lg">Secondaire</button>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="footer__grid">
<div>
<div class="footer__brand">MonSite</div>
<p class="footer__desc">Description...</p>
</div>
<div>
<h4 class="footer__heading">Liens</h4>
<a href="#" class="footer__link">Page 1</a>
<a href="#" class="footer__link">Page 2</a>
</div>
</div>
<div class="footer__bottom">© 2026 MonSite</div>
</footer>
/* FAUX — les IDs sont uniques, pas réutilisables */
#main-button { background: #2563eb; }
#header-nav { display: flex; }
/* CORRECT — les classes sont réutilisables */
.btn--primary { background: #2563eb; }
.navbar { display: flex; }
/* FAUX — un seul style, pas de flexibilité */
.btn {
background: #2563eb;
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 14px;
}
/* CORRECT — composant avec variantes et tailles */
.btn { /* styles de base */ }
.btn--primary { /* variante */ }
.btn--outline { /* variante */ }
.btn--sm { /* taille */ }
.btn--lg { /* taille */ }
/* FAUX — pas de retour visuel pour l'utilisateur */
.btn { background: #2563eb; }
/* CORRECT — tous les états couverts */
.btn { background: #2563eb; transition: all 0.2s; }
.btn:hover { background: #1d4ed8; }
.btn:focus-visible { box-shadow: 0 0 0 3px rgba(37,99,235,0.3); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* FAUX — le composant dépend du parent */
.sidebar .card { padding: 1rem; }
.nav .btn { font-size: 0.8rem; }
/* CORRECT — le composant est autonome */
.card { padding: 1.5rem; }
.card--compact { padding: 1rem; }
.btn--sm { font-size: 0.8rem; }
/* FAUX — nommage visuel */
.blue-box { background: blue; }
.rounded { border-radius: 50%; }
.shadow { box-shadow: ...; }
/* CORRECT — nommage sémantique */
.alert--info { background: #eff6ff; }
.avatar { border-radius: 50%; }
.card { box-shadow: var(--shadow-sm); }
| Critère | Vérification |
|---|---|
| Nommage BEM | .bloc__element--modificateur respecté |
| Variantes | Modificateurs pour taille, couleur, variante |
| États | hover, focus-visible, active, disabled |
| Accessibilité | Contraste AA, focus visible, rôles ARIA |
| Responsive | Testé sur mobile, tablette, desktop |
| Autonomie | Pas de dépendance au contexte parent |
| Transitions | Changements d'état fluides (transition) |
| DRY | Pas de duplication, tokens CSS utilisés |
Défi 1 : Bouton complet
Créez un composant bouton avec : 4 variantes (primary, secondary, outline, ghost), 3 tailles (sm, md, lg), état loading avec spinner CSS, état disabled, et support d'icône à gauche.
Défi 2 : Carte produit e-commerce
Créez une carte produit avec : image, badge promo (-30%), titre, note (étoiles), prix barré, prix actuel, bouton "Ajouter au panier", et un modificateur "épuisé" qui grise le tout.
Défi 3 : Formulaire complet
Créez un formulaire de connexion avec : input group (email avec icône), input group (mot de passe avec icône œil), checkbox "Se souvenir de moi", bouton "Se connecter", lien "Mot de passe oublié", et un divider "ou continuer avec" suivi de boutons sociaux.
Défi 4 : Modal avec formulaire
Créez un modal de confirmation contenant : un titre, un texte de description, un input de confirmation, et deux boutons (Annuler / Confirmer). Le modal doit avoir un overlay sombre et une animation d'entrée.
Défi 5 : Dashboard complet
Assemblez au moins 5 composants de ce cours pour créer un mini-dashboard : navbar, sidebar, 3 cartes stats, une barre de progression, et un tableau avec skeleton loaders.
Projet 1 : Design system UI complet
Créez un design system contenant tous les composants de ce chapitre, organisé en fichiers modulaires avec BEM :
Projet 2 : Landing page complète
Créez une landing page en assemblant les composants :
Projet 3 : Mini-application e-commerce
Créez une page produit e-commerce avec :
| Catégorie | Composants | Variantes principales |
|---|---|---|
| Boutons | 7 types | primary, secondary, outline, ghost, danger, success, icon |
| Cartes | 7 types | basic, product, profile, stats, blog, team, testimonial |
| Navigation | 6 types | navbar, sidebar, breadcrumbs, pagination, tabs, mega-menu |
| Feedback | 5 types | alert, toast, badge, progress, skeleton |
| Overlays | 5 types | modal, tooltip, popover, dropdown, accordion |
| Formulaires | 7 types | input-group, checkbox, radio, switch, select, range, file-upload |
| Layout | 3 types | hero, footer, container |
-- : .btn--primary, .card--featured.position: absolute + opacity/visibility sur :hover du parent.<input type="checkbox"> caché + <span> stylé en track + thumb, toggled via :checked.:checked./* ============================================
COMPOSANTS UI — Cheat Sheet
============================================ */
/* --- BOUTONS --- */
.btn { display: inline-flex; align-items: center; gap: 0.5rem;
padding: 0.55rem 1.1rem; border: 2px solid transparent;
border-radius: 8px; font-weight: 600; cursor: pointer;
transition: all 0.2s; }
.btn--primary { background: #2563eb; color: white; }
.btn--outline { background: transparent; color: #2563eb; border-color: #2563eb; }
.btn--sm { padding: 0.4rem 0.8rem; font-size: 0.8rem; }
.btn--lg { padding: 0.75rem 1.5rem; font-size: 1rem; }
/* --- CARTES --- */
.card { background: white; border: 1px solid #e2e8f0;
border-radius: 12px; overflow: hidden; }
.card__body { padding: 1.25rem; }
.card__title { font-weight: 700; margin-bottom: 0.5rem; }
/* --- ALERTES --- */
.alert { padding: 0.75rem 1rem; border-radius: 8px; display: flex; gap: 0.75rem; }
.alert--success { background: #f0fdf4; color: #166534; }
.alert--error { background: #fef2f2; color: #991b1b; }
/* --- TOOLTIP --- */
.tooltip-wrapper:hover .tooltip { opacity: 1; visibility: visible; }
.tooltip { position: absolute; bottom: calc(100% + 8px); left: 50%;
transform: translateX(-50%); background: #1e293b;
color: white; padding: 0.4rem 0.75rem; border-radius: 6px;
opacity: 0; visibility: hidden; transition: all 0.2s; }
/* --- MODAL --- */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5);
display: flex; align-items: center; justify-content: center; }
.modal { background: white; border-radius: 16px; max-width: 480px; width: 90%;
box-shadow: 0 20px 60px rgba(0,0,0,0.15); }
/* --- SWITCH --- */
.switch__track { width: 2.75rem; height: 1.5rem; background: #d1d5db;
border-radius: 9999px; position: relative; transition: background 0.2s; }
.switch input:checked + .switch__track { background: #2563eb; }
.switch__thumb { position: absolute; width: 1.2rem; height: 1.2rem;
background: white; border-radius: 50%; top: 0.15rem; left: 0.15rem;
transition: transform 0.2s; }
.switch input:checked + .switch__track .switch__thumb { transform: translateX(1.25rem); }