Architecture de Design System en pratique : Le parcours complet de construction des Design Tokens aux bibliothèques de composants

前端工程

L'Architecture à Trois Couches des Design Systems

┌─────────────────────────────────┐
│  Couche Composants              │  Button, Card, Modal...
├─────────────────────────────────┤
│  Couche Motifs                  │  Formulaires, Navigation, Tableaux de données
├─────────────────────────────────┤
│  Couche Tokens (Design Tokens)  │  Couleurs, Espacement, Tailles de police, Ombres
└─────────────────────────────────┘

Les tokens sont la fondation, les motifs sont la composition, les composants sont le point d'arrivée. Construire de bas en haut, consommer de haut en bas.


Le Système de Design Tokens

1. Tokens de Couleur

/* Tokens sémantiques à trois couches */

/* Couche 1 : Valeurs primitives */
:root {
  --blue-50: #eff6ff;
  --blue-500: #3b82f6;
  --blue-700: #1d4ed8;
  --gray-50: #f9fafb;
  --gray-900: #111827;
  --red-500: #ef4444;
  --green-500: #22c55e;
}

/* Couche 2 : Mapping sémantique */
:root {
  --color-primary: var(--blue-500);
  --color-primary-hover: var(--blue-700);
  --color-surface: var(--gray-50);
  --color-text: var(--gray-900);
  --color-danger: var(--red-500);
  --color-success: var(--green-500);
}

/* Couche 3 : Spécifique au composant */
:root {
  --button-bg: var(--color-primary);
  --button-bg-hover: var(--color-primary-hover);
  --button-text: white;
  --input-border: var(--color-border);
  --input-border-focus: var(--color-primary);
}

2. Tokens d'Espacement (Système de grille 8pt)

:root {
  --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.5rem;   /* 24px */
  --space-6: 2rem;     /* 32px */
  --space-8: 3rem;     /* 48px */
  --space-10: 4rem;    /* 64px */
}

3. Tokens de Typographie

:root {
  /* Tailles de police */
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.125rem;   /* 18px */
  --text-xl: 1.25rem;    /* 20px */
  --text-2xl: 1.5rem;    /* 24px */
  --text-3xl: 1.875rem;  /* 30px */

  /* Hauteurs de ligne */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* Graisses de police */
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;
}

4. Tokens d'Ombre

:root {
  --shadow-xs: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.06);
  --shadow-lg: 0 10px 15px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);
  --shadow-xl: 0 20px 25px rgba(0,0,0,0.1), 0 8px 10px rgba(0,0,0,0.04);
}

Mécanisme de Changement de Thème

Approche par Variables CSS

/* Thème clair (par défaut) */
:root {
  --color-surface: #ffffff;
  --color-text: #111827;
  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
}

/* Thème sombre */
[data-theme="dark"] {
  --color-surface: #1f2937;
  --color-text: #f9fafb;
  --color-border: #374151;
  --color-primary: #60a5fa;
}

/* Thème à contraste élevé */
[data-theme="high-contrast"] {
  --color-surface: #000000;
  --color-text: #ffffff;
  --color-border: #ffffff;
  --color-primary: #ffff00;
}

Changement de Thème en React

import { useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'system';

function useTheme() {
  const [theme, setTheme] = useState<Theme>(() => {
    return (localStorage.getItem('theme') as Theme) || 'system';
  });

  useEffect(() => {
    const root = document.documentElement;
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    const resolved = theme === 'system' ? (systemDark ? 'dark' : 'light') : theme;

    root.setAttribute('data-theme', resolved);
    localStorage.setItem('theme', theme);
  }, [theme]);

  return { theme, setTheme };
}

Mode Sombre avec Tailwind CSS v4

/* tailwind.config.ts en mode variables CSS */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
<!-- Auto-responsive au thème -->
<div class="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100">
  Le contenu s'adapte automatiquement au thème actuel
</div>

Principes de Conception d'API de Composants

1. Responsabilité Unique + Composition d'Abord

// ❌ Sur-encapsulation : Toutes les fonctionnalités fourrées dans un composant
<SuperButton variant="primary" size="lg" icon="save" loading disabled />

// ✅ Pattern de composition : Assembler via des composants enfants
<Button variant="primary" size="lg">
  <Icon name="save" />
  <Button.Label>Enregistrer</Button.Label>
  <Button.Loading />
</Button>

2. Contrôlé vs Non Contrôlé

// Mode contrôlé : État géré externement
<Input value={value} onChange={setValue} />

// Mode non contrôlé : État géré en interne par le composant
<Input defaultValue="hello" onChange={handleChange} />

// Compatible avec les deux
interface InputProps {
  value?: string;          // Contrôlé
  defaultValue?: string;   // Non contrôlé
  onChange?: (value: string) => void;
}

3. Composants Polymorphes

// Button peut s'afficher comme button, a, ou Link de Next.js
type ButtonProps<C extends React.ElementType = 'button'> = {
  as?: C;
} & React.ComponentPropsWithoutRef<C>;

function Button<C extends React.ElementType = 'button'>(
  { as, ...props }: ButtonProps<C>
) {
  const Component = as || 'button';
  return <Component className="btn" {...props} />;
}

// Utilisation
<Button>Cliquer</Button>
<Button as="a" href="/link">Lien</Button>
<Button as={Link} to="/page">Lien de route</Button>

4. Pattern Slots

// Design Slots du composant Card
interface CardSlots {
  header?: React.ReactNode;
  media?: React.ReactNode;
  content: React.ReactNode;
  footer?: React.ReactNode;
  actions?: React.ReactNode;
}

function Card({ header, media, content, footer, actions }: CardSlots) {
  return (
    <div className="card">
      {header && <div className="card-header">{header}</div>}
      {media && <div className="card-media">{media}</div>}
      <div className="card-content">{content}</div>
      {footer && <div className="card-footer">{footer}</div>}
      {actions && <div className="card-actions">{actions}</div>}
    </div>
  );
}

Adaptation Multi-Marque

Couche de Surcharge de Marque

/* Tokens de base (Marque A) */
:root {
  --brand-primary: #3b82f6;
  --brand-radius: 8px;
  --brand-font: 'Inter', sans-serif;
}

/* Surcharge Marque B */
[data-brand="b"] {
  --brand-primary: #8b5cf6;
  --brand-radius: 12px;
  --brand-font: 'Poppins', sans-serif;
}

/* Surcharge Marque C */
[data-brand="c"] {
  --brand-primary: #f59e0b;
  --brand-radius: 4px;
  --brand-font: 'Roboto', sans-serif;
}

Pipeline de Compilation des Tokens

Fichiers sources de design (Figma)
  ↓
Compilation Style Dictionary
  ↓
├── Variables CSS (Web)
├── Configuration Tailwind (Projets Tailwind)
├── Fichiers Swift iOS
├── Fichiers Kotlin Android
└── JSON (Site de documentation)
// style-dictionary.config.js
const StyleDictionary = require('style-dictionary');

module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      buildPath: 'dist/css/',
      files: [{ destination: 'variables.css', format: 'css/variables' }],
    },
    tailwind: {
      transformGroup: 'js',
      buildPath: 'dist/tailwind/',
      files: [{ destination: 'theme.js', format: 'javascript/module' }],
    },
  },
};

Pratiques du Design System de ToolsKu

ToolsKu utilise Tailwind CSS v4 + variables CSS pour construire son design system :

/* Tokens de thème ToolsKu */
:root {
  --color-primary: #2563eb;
  --color-surface: #ffffff;
  --color-text: #1f2937;
  --radius: 8px;
  --font-sans: 'Inter', system-ui, sans-serif;
}

/* Composant carte d'outil */
.tool-card {
  padding: var(--space-4);
  border-radius: var(--radius);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  transition: transform 0.2s, box-shadow 0.2s;
}

.tool-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

Gouvernance du Design System

Gestion des Versions

@toolsku/tokens     v2.4.0  → Package Design Tokens
@toolsku/components v3.1.0  → Bibliothèque de composants
@toolsku/patterns   v1.2.0  → Bibliothèque de motifs

Chaîne de dépendances : patterns → components → tokens

Workflow de Changement

1. Le designer modifie les tokens dans Figma
2. Auto-synchronisation vers le dépôt Git (Plugin Figma)
3. CI exécute la compilation Style Dictionary
4. Tests de régression visuelle (Chromatic/Storybook)
5. Auto-publication de la nouvelle version (Changesets)

Site de Documentation

Storybook
├── Tokens      → Vitrine couleurs, espacement, tailles de police
├── Components  → Documentation d'interaction des composants
├── Patterns    → Guide d'utilisation des motifs
└── Guidelines  → Documentation des spécifications de design

Résumé

Un design system n'est pas seulement une bibliothèque de composants, mais une architecture complète des Tokens aux Composants aux Motifs. Les tokens à trois couches (Primitif → Sémantique → Composant) sont le fondement de l'évolutivité. Les patterns de composition surpassent la sur-encapsulation, et les variables CSS + Tailwind est le choix technologique le plus pratique en 2026. Rappelez-vous : un bon design system permet aux designers et développeurs de parler le même langage, et les tokens sont le vocabulaire de ce langage.

Essayez ces outils exécutés localement dans le navigateur — aucune inscription requise →

#设计系统#Design Token#组件库#Tailwind CSS#前端架构