Introduction
Un système de design (design system) est bien plus qu’une simple bibliothèque de composants. C’est un langage visuel unifié, un ensemble de règles, de principes et de composants réutilisables qui garantissent la cohérence à travers tous vos produits digitaux.
Des entreprises comme Airbnb, Shopify, ou Atlassian ont transformé leur workflow en créant des design systems robustes. Dans cet article, vous apprendrez à créer votre propre système de design, de la définition des fondations jusqu’à la documentation et la maintenance.
Qu’est-ce qu’un système de design ?
Définition et composantes
Un design system complet comprend :
- Design Tokens – Variables de design (couleurs, espacements, typographie)
- Composants UI – Éléments d’interface réutilisables
- Patterns – Solutions de design récurrentes
- Guidelines – Règles d’utilisation et bonnes pratiques
- Documentation – Comment utiliser le système
- Code – Implémentation technique
- Cohérence : Interface unifiée à travers tous les produits
- Efficacité : Réduction du temps de design et développement
- Scalabilité : Facilite la croissance de l’équipe et du produit
- Collaboration : Langage commun entre designers et développeurs
- Qualité : Standards élevés maintenus automatiquement
- Accessibilité : Conformité WCAG intégrée dès le départ
Pourquoi créer un design system ?
Avantages :
Étape 1 : Les fondations (Design Tokens)
Les design tokens sont les valeurs atomiques de votre design : couleurs, espacements, typographie, ombres, etc.
Structure de fichier CSS Variables
/* design-tokens.css */
:root {
/* ============================================
COULEURS - Palette complète
============================================ */
/* Primaire - Bleu */
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-200: #bfdbfe;
--color-primary-300: #93c5fd;
--color-primary-400: #60a5fa;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
--color-primary-800: #1e40af;
--color-primary-900: #1e3a8a;
/* Neutrals - Gris */
--color-neutral-50: #f9fafb;
--color-neutral-100: #f3f4f6;
--color-neutral-200: #e5e7eb;
--color-neutral-300: #d1d5db;
--color-neutral-400: #9ca3af;
--color-neutral-500: #6b7280;
--color-neutral-600: #4b5563;
--color-neutral-700: #374151;
--color-neutral-800: #1f2937;
--color-neutral-900: #111827;
/* Sémantiques */
--color-success-50: #f0fdf4;
--color-success-500: #22c55e;
--color-success-700: #15803d;
--color-error-50: #fef2f2;
--color-error-500: #ef4444;
--color-error-700: #b91c1c;
--color-warning-50: #fffbeb;
--color-warning-500: #f59e0b;
--color-warning-700: #b45309;
--color-info-50: #eff6ff;
--color-info-500: #3b82f6;
--color-info-700: #1d4ed8;
/* ============================================
COULEURS - Tokens sémantiques
============================================ */
/* Texte */
--text-primary: var(--color-neutral-900);
--text-secondary: var(--color-neutral-700);
--text-tertiary: var(--color-neutral-500);
--text-disabled: var(--color-neutral-400);
--text-inverse: #ffffff;
--text-link: var(--color-primary-600);
--text-link-hover: var(--color-primary-700);
/* Arrière-plans */
--bg-primary: #ffffff;
--bg-secondary: var(--color-neutral-50);
--bg-tertiary: var(--color-neutral-100);
--bg-inverse: var(--color-neutral-900);
--bg-overlay: rgba(0, 0, 0, 0.5);
/* Bordures */
--border-light: var(--color-neutral-200);
--border-medium: var(--color-neutral-300);
--border-strong: var(--color-neutral-400);
--border-focus: var(--color-primary-500);
/* ============================================
TYPOGRAPHIE
============================================ */
/* Familles de polices */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
--font-serif: Georgia, 'Times New Roman', Times, serif;
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas,
'Courier New', monospace;
/* Tailles de police */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-size-5xl: 3rem; /* 48px */
--font-size-6xl: 3.75rem; /* 60px */
/* Poids de police */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
/* Hauteurs de ligne */
--line-height-tight: 1.25;
--line-height-snug: 1.375;
--line-height-normal: 1.5;
--line-height-relaxed: 1.625;
--line-height-loose: 2;
/* Espacement lettres */
--letter-spacing-tight: -0.025em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.025em;
--letter-spacing-wider: 0.05em;
--letter-spacing-widest: 0.1em;
/* ============================================
ESPACEMENTS (Échelle basée sur 4px)
============================================ */
--space-0: 0;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */
--space-32: 8rem; /* 128px */
/* ============================================
TAILLES
============================================ */
/* Largeurs */
--width-xs: 20rem; /* 320px */
--width-sm: 24rem; /* 384px */
--width-md: 28rem; /* 448px */
--width-lg: 32rem; /* 512px */
--width-xl: 36rem; /* 576px */
--width-2xl: 42rem; /* 672px */
--width-3xl: 48rem; /* 768px */
--width-4xl: 56rem; /* 896px */
--width-5xl: 64rem; /* 1024px */
--width-6xl: 72rem; /* 1152px */
--width-7xl: 80rem; /* 1280px */
/* Hauteurs */
--height-screen: 100vh;
--height-header: 4rem;
--height-footer: 20rem;
/* ============================================
BORDURES
============================================ */
/* Largeurs */
--border-width-0: 0;
--border-width-1: 1px;
--border-width-2: 2px;
--border-width-4: 4px;
--border-width-8: 8px;
/* Rayons */
--border-radius-none: 0;
--border-radius-sm: 0.125rem; /* 2px */
--border-radius-base: 0.25rem; /* 4px */
--border-radius-md: 0.375rem; /* 6px */
--border-radius-lg: 0.5rem; /* 8px */
--border-radius-xl: 0.75rem; /* 12px */
--border-radius-2xl: 1rem; /* 16px */
--border-radius-3xl: 1.5rem; /* 24px */
--border-radius-full: 9999px;
/* ============================================
OMBRES
============================================ */
--shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--shadow-base: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
--shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
--shadow-2xl: 0 30px 60px -15px rgba(0, 0, 0, 0.3);
--shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.05);
/* ============================================
Z-INDEX (Layering)
============================================ */
--z-index-dropdown: 1000;
--z-index-sticky: 1020;
--z-index-fixed: 1030;
--z-index-modal-backdrop: 1040;
--z-index-modal: 1050;
--z-index-popover: 1060;
--z-index-tooltip: 1070;
/* ============================================
TRANSITIONS
============================================ */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
--transition-slower: 500ms;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
/* ============================================
BREAKPOINTS (pour JavaScript)
============================================ */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
/* ============================================
MODE SOMBRE
============================================ */
@media (prefers-color-scheme: dark) {
:root {
/* Inversions pour mode sombre */
--text-primary: var(--color-neutral-50);
--text-secondary: var(--color-neutral-300);
--text-tertiary: var(--color-neutral-400);
--bg-primary: var(--color-neutral-900);
--bg-secondary: var(--color-neutral-800);
--bg-tertiary: var(--color-neutral-700);
--border-light: var(--color-neutral-700);
--border-medium: var(--color-neutral-600);
--border-strong: var(--color-neutral-500);
}
}
/* Classe pour forcer le mode sombre */
[data-theme="dark"] {
--text-primary: var(--color-neutral-50);
--text-secondary: var(--color-neutral-300);
--text-tertiary: var(--color-neutral-400);
--bg-primary: var(--color-neutral-900);
--bg-secondary: var(--color-neutral-800);
--bg-tertiary: var(--color-neutral-700);
--border-light: var(--color-neutral-700);
--border-medium: var(--color-neutral-600);
--border-strong: var(--color-neutral-500);
}
Étape 2 : Les composants de base
Créons une bibliothèque de composants réutilisables.
Boutons
/* components/button.css */
.btn {
/* Base */
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
/* Typographie */
font-family: var(--font-sans);
font-weight: var(--font-weight-semibold);
text-decoration: none;
white-space: nowrap;
/* Bordures */
border: var(--border-width-1) solid transparent;
border-radius: var(--border-radius-lg);
/* Interactivité */
cursor: pointer;
user-select: none;
transition: all var(--transition-base) var(--ease-out);
/* États désactivés */
&:disabled,
&[aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
/* Focus visible (accessibilité) */
&:focus-visible {
outline: 2px solid var(--border-focus);
outline-offset: 2px;
}
}
/* Variantes de taille */
.btn-sm {
padding: var(--space-2) var(--space-3);
font-size: var(--font-size-sm);
min-height: 2rem;
}
.btn-md {
padding: var(--space-3) var(--space-4);
font-size: var(--font-size-base);
min-height: 2.5rem;
}
.btn-lg {
padding: var(--space-4) var(--space-6);
font-size: var(--font-size-lg);
min-height: 3rem;
}
/* Variantes de style */
.btn-primary {
background-color: var(--color-primary-600);
color: white;
border-color: var(--color-primary-600);
&:hover:not(:disabled) {
background-color: var(--color-primary-700);
border-color: var(--color-primary-700);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
&:active:not(:disabled) {
transform: translateY(0);
box-shadow: var(--shadow-sm);
}
}
.btn-secondary {
background-color: transparent;
color: var(--color-primary-600);
border-color: var(--color-primary-600);
&:hover:not(:disabled) {
background-color: var(--color-primary-50);
border-color: var(--color-primary-700);
color: var(--color-primary-700);
}
}
.btn-tertiary {
background-color: transparent;
color: var(--text-secondary);
border-color: transparent;
&:hover:not(:disabled) {
background-color: var(--bg-secondary);
color: var(--text-primary);
}
}
.btn-danger {
background-color: var(--color-error-600);
color: white;
border-color: var(--color-error-600);
&:hover:not(:disabled) {
background-color: var(--color-error-700);
border-color: var(--color-error-700);
}
}
/* Variantes de largeur */
.btn-full {
width: 100%;
}
.btn-auto {
width: auto;
}
/* Groupe de boutons */
.btn-group {
display: inline-flex;
gap: var(--space-2);
}
.btn-group-vertical {
display: flex;
flex-direction: column;
}
<!-- Exemples d'utilisation -->
<button class="btn btn-primary btn-md">
Enregistrer
</button>
<button class="btn btn-secondary btn-lg">
Annuler
</button>
<button class="btn btn-tertiary btn-sm" disabled>
Désactivé
</button>
<a href="/action" class="btn btn-primary btn-md">
Lien bouton
</a>
<!-- Bouton avec icône -->
<button class="btn btn-primary btn-md">
<svg width="20" height="20" fill="currentColor">
<path d="M10 5l7 7-7 7"></path>
</svg>
Suivant
</button>
<!-- Groupe de boutons -->
<div class="btn-group">
<button class="btn btn-secondary btn-md">Précédent</button>
<button class="btn btn-primary btn-md">Suivant</button>
</div>
Inputs (Champs de formulaire)
/* components/input.css */
.input-wrapper {
display: flex;
flex-direction: column;
gap: var(--space-2);
width: 100%;
}
.input-label {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--text-primary);
}
.input-label-required::after {
content: '*';
color: var(--color-error-500);
margin-left: var(--space-1);
}
.input {
/* Base */
width: 100%;
padding: var(--space-3) var(--space-4);
/* Typographie */
font-family: var(--font-sans);
font-size: var(--font-size-base);
line-height: var(--line-height-normal);
color: var(--text-primary);
/* Bordures */
border: var(--border-width-1) solid var(--border-medium);
border-radius: var(--border-radius-lg);
/* Arrière-plan */
background-color: var(--bg-primary);
/* Transitions */
transition: all var(--transition-base) var(--ease-out);
/* Placeholder */
&::placeholder {
color: var(--text-tertiary);
}
/* Focus */
&:focus {
outline: none;
border-color: var(--border-focus);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
/* Hover */
&:hover:not(:focus):not(:disabled) {
border-color: var(--border-strong);
}
/* Disabled */
&:disabled {
background-color: var(--bg-secondary);
color: var(--text-disabled);
cursor: not-allowed;
opacity: 0.6;
}
/* Erreur */
&[aria-invalid="true"],
&.input-error {
border-color: var(--color-error-500);
}
&[aria-invalid="true"]:focus,
&.input-error:focus {
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}
/* Succès */
&.input-success {
border-color: var(--color-success-500);
}
&.input-success:focus {
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.1);
}
}
/* Tailles */
.input-sm {
padding: var(--space-2) var(--space-3);
font-size: var(--font-size-sm);
}
.input-lg {
padding: var(--space-4) var(--space-5);
font-size: var(--font-size-lg);
}
/* Textarea */
.textarea {
resize: vertical;
min-height: 6rem;
}
/* Messages d'aide et d'erreur */
.input-hint {
font-size: var(--font-size-sm);
color: var(--text-tertiary);
}
.input-error-message {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--font-size-sm);
color: var(--color-error-700);
}
.input-success-message {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--font-size-sm);
color: var(--color-success-700);
}
/* Input avec icône */
.input-with-icon {
position: relative;
}
.input-with-icon .input {
padding-left: var(--space-10);
}
.input-icon {
position: absolute;
left: var(--space-3);
top: 50%;
transform: translateY(-50%);
color: var(--text-tertiary);
pointer-events: none;
}
<!-- Exemples d'utilisation -->
<!-- Input basique -->
<div class="input-wrapper">
<label for="email" class="input-label input-label-required">
Email
</label>
<input
type="email"
id="email"
class="input"
placeholder="vous@exemple.com"
required
>
Nous ne partagerons jamais votre email
</div>
<!-- Input avec erreur -->
<div class="input-wrapper">
<label for="password" class="input-label">
Mot de passe
</label>
<input
type="password"
id="password"
class="input input-error"
aria-invalid="true"
aria-describedby="password-error"
>
</div>
<!-- Input avec succès -->
<div class="input-wrapper">
<label for="username" class="input-label">
Nom d'utilisateur
</label>
<input
type="text"
id="username"
class="input input-success"
value="johndoe"
>
</div>
<!-- Input avec icône -->
<div class="input-wrapper">
<label for="search" class="input-label">
Rechercher
</label>
<div class="input-with-icon">
<input
type="search"
id="search"
class="input"
placeholder="Rechercher..."
>
</div>
</div>
<!-- Textarea -->
<div class="input-wrapper">
<label for="message" class="input-label">
Message
</label>
<textarea
id="message"
class="input textarea"
rows="5"
placeholder="Votre message..."
></textarea>
</div>
<!-- Select -->
<div class="input-wrapper">
<label for="country" class="input-label">
Pays
</label>
<select id="country" class="input">
<option value="">Sélectionnez un pays</option>
<option value="be">Belgique</option>
<option value="ch">Suisse</option>
<option value="ca">Canada</option>
</select>
</div>
Cards (Cartes)
/* components/card.css */
.card {
background: var(--bg-primary);
border: var(--border-width-1) solid var(--border-light);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-sm);
overflow: hidden;
transition: all var(--transition-base) var(--ease-out);
}
.card:hover {
box-shadow: var(--shadow-md);
}
/* Card interactive (cliquable) */
.card-interactive {
cursor: pointer;
}
.card-interactive:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.card-interactive:active {
transform: translateY(-2px);
}
/* Parties de la carte */
.card-header {
padding: var(--space-6);
border-bottom: var(--border-width-1) solid var(--border-light);
}
.card-body {
padding: var(--space-6);
}
.card-footer {
padding: var(--space-6);
border-top: var(--border-width-1) solid var(--border-light);
background: var(--bg-secondary);
}
/* Card avec image */
.card-image {
width: 100%;
height: auto;
display: block;
}
.card-image-container {
position: relative;
aspect-ratio: 16/9;
overflow: hidden;
background: var(--bg-secondary);
}
.card-image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Éléments de contenu */
.card-title {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-bold);
line-height: var(--line-height-tight);
color: var(--text-primary);
margin-bottom: var(--space-2);
}
.card-subtitle {
font-size: var(--font-size-sm);
color: var(--text-tertiary);
margin-bottom: var(--space-4);
}
.card-description {
font-size: var(--font-size-base);
line-height: var(--line-height-relaxed);
color: var(--text-secondary);
}
/* Variantes */
.card-outlined {
border-width: var(--border-width-2);
box-shadow: none;
}
.card-elevated {
border: none;
box-shadow: var(--shadow-lg);
}
.card-flat {
border: none;
box-shadow: none;
}
/* Card avec accent coloré */
.card-accent {
border-top: 4px solid var(--color-primary-500);
}
.card-accent-success {
border-top-color: var(--color-success-500);
}
.card-accent-error {
border-top-color: var(--color-error-500);
}
.card-accent-warning {
border-top-color: var(--color-warning-500);
}
/* Grille de cartes */
.card-grid {
display: grid;
gap: var(--space-6);
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
<!-- Carte basique -->
<div class="card">
<div class="card-body">
<h3 class="card-title">Titre de la carte</h3>
<p class="card-description">
Description du contenu de la carte avec quelques détails supplémentaires.
</p>
</div>
</div>
<!-- Carte complète avec image -->
<div class="card card-interactive">
<div class="card-image-container">
<img src="image.jpg" alt="Description" class="card-image">
</div>
<div class="card-body">
<div class="card-subtitle">Catégorie</div>
<h3 class="card-title">Titre de l'article</h3>
<p class="card-description">
Un aperçu intéressant du contenu qui donne envie d'en savoir plus.
</p>
</div>
<div class="card-footer">
<a href="#" class="btn btn-primary btn-sm">Lire la suite</a>
</div>
</div>
<!-- Carte avec accent -->
<div class="card card-accent-success">
<div class="card-header">
<h3 class="card-title">Compte vérifié</h3>
</div>
<div class="card-body">
<p class="card-description">
Votre compte a été vérifié avec succès. Vous pouvez maintenant accéder à toutes les fonctionnalités.
</p>
</div>
</div>
<!-- Grille de cartes -->
<div class="card-grid">
<div class="card">...</div>
<div class="card">...</div>
<div class="card">...</div>
</div>
Étape 3 : Composants complexes
Modal (Dialogue)
/* components/modal.css */
.modal-overlay {
position: fixed;
inset: 0;
background: var(--bg-overlay);
z-index: var(--z-index-modal-backdrop);
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-4);
opacity: 0;
visibility: hidden;
transition: all var(--transition-base) var(--ease-out);
}
.modal-overlay.is-open {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--bg-primary);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-2xl);
max-width: var(--width-lg);
width: 100%;
max-height: 90vh;
overflow: hidden;
display: flex;
flex-direction: column;
transform: scale(0.95);
transition: transform var(--transition-base) var(--ease-out);
z-index: var(--z-index-modal);
}
.modal-overlay.is-open .modal {
transform: scale(1);
}
.modal-header {
padding: var(--space-6);
border-bottom: var(--border-width-1) solid var(--border-light);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-bold);
color: var(--text-primary);
}
.modal-close {
width: 2.5rem;
height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--border-radius-base);
border: none;
background: transparent;
color: var(--text-secondary);
cursor: pointer;
transition: all var(--transition-fast) var(--ease-out);
}
.modal-close:hover {
background: var(--bg-secondary);
color: var(--text-primary);
}
.modal-body {
padding: var(--space-6);
overflow-y: auto;
flex: 1;
}
.modal-footer {
padding: var(--space-6);
border-top: var(--border-width-1) solid var(--border-light);
display: flex;
gap: var(--space-3);
justify-content: flex-end;
}
/* Tailles */
.modal-sm .modal {
max-width: var(--width-sm);
}
.modal-md .modal {
max-width: var(--width-md);
}
.modal-lg .modal {
max-width: var(--width-lg);
}
.modal-xl .modal {
max-width: var(--width-2xl);
}
.modal-full .modal {
max-width: 95vw;
max-height: 95vh;
}
<!-- Modal HTML -->
<div class="modal-overlay" id="exampleModal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="modal-title">Titre du modal</h2>
<button class="modal-close" aria-label="Fermer">
<svg width="20" height="20" fill="currentColor">
<path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"/>
</svg>
</button>
</div>
<div class="modal-body">
<p>Contenu du modal...</p>
</div>
<div class="modal-footer">
<button class="btn btn-tertiary btn-md">Annuler</button>
<button class="btn btn-primary btn-md">Confirmer</button>
</div>
</div>
</div>
// modal.js - Gestion du modal
class Modal {
constructor(modalId) {
this.modal = document.getElementById(modalId);
this.closeBtn = this.modal.querySelector('.modal-close');
this.overlay = this.modal;
this.init();
}
init() {
// Fermer avec bouton
this.closeBtn?.addEventListener('click', () => this.close());
// Fermer en cliquant sur l'overlay
this.overlay.addEventListener('click', (e) => {
if (e.target === this.overlay) {
this.close();
}
});
// Fermer avec Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen()) {
this.close();
}
});
}
open() {
this.modal.classList.add('is-open');
document.body.style.overflow = 'hidden';
// Focus trap
const focusableElements = this.modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
firstElement?.focus();
this.modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
}
close() {
this.modal.classList.remove('is-open');
document.body.style.overflow = '';
}
isOpen() {
return this.modal.classList.contains('is-open');
}
}
// Utilisation
const myModal = new Modal('exampleModal');
// Ouvrir le modal
document.getElementById('openModal').addEventListener('click', () => {
myModal.open();
});
Étape 4 : Documentation
Créez une page de documentation interactive.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Design System - Documentation</title>
<link rel="stylesheet" href="design-tokens.css">
<link rel="stylesheet" href="components.css">
<style>
/* Documentation spécifique */
.doc-container {
max-width: 1200px;
margin: 0 auto;
padding: var(--space-8) var(--space-6);
}
.doc-sidebar {
position: sticky;
top: var(--space-6);
max-height: calc(100vh - var(--space-12));
overflow-y: auto;
}
.doc-nav {
list-style: none;
}
.doc-nav a {
display: block;
padding: var(--space-2) var(--space-4);
color: var(--text-secondary);
text-decoration: none;
border-radius: var(--border-radius-base);
transition: all var(--transition-fast);
}
.doc-nav a:hover {
background: var(--bg-secondary);
color: var(--text-primary);
}
.doc-nav a.active {
background: var(--color-primary-50);
color: var(--color-primary-700);
font-weight: var(--font-weight-semibold);
}
.doc-section {
margin-bottom: var(--space-16);
}
.doc-section-title {
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
margin-bottom: var(--space-6);
padding-bottom: var(--space-4);
border-bottom: 2px solid var(--border-light);
}
.doc-component-title {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
margin-top: var(--space-8);
margin-bottom: var(--space-4);
}
.doc-example {
padding: var(--space-8);
background: var(--bg-secondary);
border-radius: var(--border-radius-lg);
margin-bottom: var(--space-4);
}
.doc-code {
background: var(--color-neutral-900);
color: var(--color-neutral-50);
padding: var(--space-4);
border-radius: var(--border-radius-lg);
overflow-x: auto;
font-family: var(--font-mono);
font-size: var(--font-size-sm);
line-height: var(--line-height-relaxed);
}
.doc-color-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: var(--space-4);
}
.doc-color-swatch {
height: 100px;
border-radius: var(--border-radius-lg);
display: flex;
align-items: flex-end;
padding: var(--space-3);
box-shadow: var(--shadow-sm);
}
.doc-color-label {
background: var(--bg-primary);
padding: var(--space-1) var(--space-2);
border-radius: var(--border-radius-sm);
font-size: var(--font-size-xs);
font-family: var(--font-mono);
}
.doc-grid {
display: grid;
grid-template-columns: 250px 1fr;
gap: var(--space-8);
}
@media (max-width: 1024px) {
.doc-grid {
grid-template-columns: 1fr;
}
.doc-sidebar {
position: static;
}
}
</style>
</head>
<body>
<div class="doc-container">
<div class="doc-grid">
<!-- Sidebar Navigation -->
<aside class="doc-sidebar">
<h2 style="font-size: var(--font-size-xl); margin-bottom: var(--space-4);">
Design System
</h2>
<nav>
<ul class="doc-nav">
- <a href="#introduction" class="active">Introduction</a>
- <a href="#colors">Couleurs</a>
- <a href="#typography">Typographie</a>
- <a href="#spacing">Espacements</a>
- <a href="#buttons">Boutons</a>
- <a href="#inputs">Inputs</a>
- <a href="#cards">Cartes</a>
- <a href="#modals">Modales</a>
</nav>
</aside>
<!-- Main Content -->
<main>
<!-- Introduction -->
<section id="introduction" class="doc-section">
<h1 class="doc-section-title">Design System</h1>
<p style="font-size: var(--font-size-lg); line-height: var(--line-height-relaxed); color: var(--text-secondary);">
Bienvenue dans notre design system. Cette bibliothèque contient tous les composants,
styles et guidelines pour créer des interfaces cohérentes et accessibles.
</p>
</section>
<!-- Couleurs -->
<section id="colors" class="doc-section">
<h2 class="doc-section-title">Couleurs</h2>
<h3 class="doc-component-title">Couleur primaire</h3>
<div class="doc-color-grid">
<div class="doc-color-swatch" style="background: var(--color-primary-100);">
primary-100
</div>
<div class="doc-color-swatch" style="background: var(--color-primary-300);">
primary-300
</div>
<div class="doc-color-swatch" style="background: var(--color-primary-500);">
primary-500
</div>
<div class="doc-color-swatch" style="background: var(--color-primary-700);">
primary-700
</div>
<div class="doc-color-swatch" style="background: var(--color-primary-900);">
primary-900
</div>
</div>
</section>
<!-- Boutons -->
<section id="buttons" class="doc-section">
<h2 class="doc-section-title">Boutons</h2>
<h3 class="doc-component-title">Variantes</h3>
<div class="doc-example">
<div style="display: flex; gap: var(--space-3); flex-wrap: wrap;">
<button class="btn btn-primary btn-md">Primary</button>
<button class="btn btn-secondary btn-md">Secondary</button>
<button class="btn btn-tertiary btn-md">Tertiary</button>
<button class="btn btn-danger btn-md">Danger</button>
</div>
</div>
<div class="doc-code"><button class="btn btn-primary btn-md">Primary</button>
<button class="btn btn-secondary btn-md">Secondary</button>
<button class="btn btn-tertiary btn-md">Tertiary</button>
<button class="btn btn-danger btn-md">Danger</button></div>
<h3 class="doc-component-title">Tailles</h3>
<div class="doc-example">
<div style="display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap;">
<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>
</div>
</div>
</section>
<!-- Inputs -->
<section id="inputs" class="doc-section">
<h2 class="doc-section-title">Inputs</h2>
<div class="doc-example">
<div style="max-width: 400px;">
<div class="input-wrapper">
<label for="demo-email" class="input-label input-label-required">
Email
</label>
<input
type="email"
id="demo-email"
class="input"
placeholder="vous@exemple.com"
>
Nous ne partagerons jamais votre email
</div>
</div>
</div>
</section>
<!-- Cards -->
<section id="cards" class="doc-section">
<h2 class="doc-section-title">Cartes</h2>
<div class="doc-example">
<div class="card-grid">
<div class="card">
<div class="card-body">
<h3 class="card-title">Carte basique</h3>
<p class="card-description">
Une carte simple avec titre et description.
</p>
</div>
</div>
<div class="card card-accent">
<div class="card-body">
<h3 class="card-title">Carte avec accent</h3>
<p class="card-description">
Une carte avec une bordure supérieure colorée.
</p>
</div>
</div>
<div class="card card-elevated">
<div class="card-body">
<h3 class="card-title">Carte élevée</h3>
<p class="card-description">
Une carte avec une ombre plus prononcée.
</p>
</div>
</div>
</div>
</div>
</section>
</main>
</div>
</div>
</body>
</html>
Étape 5 : Maintenance et évolution
Versionning
{
"name": "my-design-system",
"version": "1.2.0",
"description": "Design system de MonEntreprise",
"changelog": {
"1.2.0": {
"date": "2024-12-18",
"changes": [
"Ajout du composant Modal",
"Amélioration de l'accessibilité des boutons",
"Nouveaux tokens de couleur pour le mode sombre"
]
},
"1.1.0": {
"date": "2024-11-15",
"changes": [
"Ajout du composant Card",
"Refonte des inputs",
"Corrections de bugs"
]
}
}
}
Processus de mise à jour
Outils recommandés
Exemples du monde réel
Material Design (Google)
Polaris (Shopify)
Carbon (IBM)
Ant Design (Alibaba)
Checklist de lancement
Avant de déployer votre design system :
Fondations :
Composants :
Documentation :
Technique :
Équipe :
Conclusion
Créer un design system est un investissement qui paie rapidement en termes de cohérence, d’efficacité et de qualité. Commencez petit avec les composants essentiels, documentez au fur et à mesure, et faites évoluer votre système en fonction des besoins réels de vos utilisateurs.
Points clés à retenir :
Prochaines étapes :
Un design system vivant et bien maintenu devient l’atout le plus précieux d’une équipe produit !
Ressources complémentaires :