Un design system mature dépasse la simple librairie de composants visuels. Il s'agit d'un écosystème cohérent où tokens, variables CSS, conventions de nommage et architekture Angular se conjuguent pour offrir une expérience développeur fluide et une cohérence visuelle garantie. Beaucoup d'équipes commencent par « créons des boutons et des cartes réutilisables », puis se retrouvent avec un chaos de surcharges, d'exceptions et de styles orphelins après six mois. La vraie différence ? Traiter le design system comme une source de vérité unique, pilotée par des tokens de design plutôt que par des classes Tailwind écrites à la main dans chaque composant.
Tokens de design : la fondation
Les tokens de design sont des valeurs atomiques (couleurs, espacements, typographies, rayons, ombres) définies une fois et réutilisées partout. Contrairement à une classe CSS classique, un token est un concept abstrait : au lieu de coder text-blue-600 directement, vous définissez color-primary qui *peut* être bleu en mode clair et violet en mode sombre. Cette abstraction permet au design system d'évoluer sans toucher au code composant. Tailwind, par défaut, est rigide sur ce point : ses classes sont générées à partir d'un fichier tailwind.config.js statique. Pour un vrai theming dynamique, il faut enrichir cette approche en définissant des tokens en tant que variables CSS personnalisées et en configurant Tailwind pour les consommer.
Concrètement, commencez par structurer vos tokens en hiérarchie : tokens primitifs (valeurs brutes), tokens de système (sémantiques) et tokens de composant. Exemple : --color-blue-500 (primitif) → --color-primary (système) → --button-bg-color (composant). En SCSS ou CSS pur, cela ressemble à ceci :
:root {
--color-neutral-100: #ffffff;
--color-neutral-900: #0f0f0f;
--color-primary: var(--color-blue-600);
--color-secondary: var(--color-indigo-600);
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--font-family-sans: 'Inter', sans-serif;
--font-size-base: 1rem;
--font-weight-regular: 400;
--font-weight-bold: 700;
}
[data-theme="dark"] {
--color-primary: var(--color-blue-400);
--color-neutral-100: var(--color-neutral-900);
--color-neutral-900: var(--color-neutral-100);
} Cette structure permet de basculer les thèmes en changeant l'attribut data-theme sur l'élément racine, sans recompiler Tailwind.
Intégration Tailwind + variables CSS
Tailwind offre une intégration native aux variables CSS via la fonction theme() dans sa config. L'idée est de remplacer les valeurs hardcodées par des références aux tokens. Voici comment configurer tailwind.config.js :
export default {
theme: {
colors: {
primary: 'var(--color-primary)',
secondary: 'var(--color-secondary)',
neutral: {
100: 'var(--color-neutral-100)',
900: 'var(--color-neutral-900)',
},
},
spacing: {
xs: 'var(--spacing-xs)',
sm: 'var(--spacing-sm)',
md: 'var(--spacing-md)',
},
fontFamily: {
sans: 'var(--font-family-sans)',
},
fontSize: {
base: 'var(--font-size-base)',
},
fontWeight: {
regular: 'var(--font-weight-regular)',
bold: 'var(--font-weight-bold)',
},
},
}; Maintenant, vos classes Tailwind comme bg-primary , text-neutral-100 et p-md consomment directement les variables CSS. Cela signifie que changer --color-primary à l'exécution affecte instantanément tous les composants, sans recompilation.
Composants Angular et composition de tokens
Dans vos composants Angular, vous pouvez alors vous concentrer sur la logique et la structure plutôt que sur les détails visuels. Un composant bouton typique :
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-button',
template: `
<button
[class]="'px-md py-sm font-bold rounded-md transition-colors ' + variantClass"
[disabled]="disabled"
>
<ng-content></ng-content>
</button>
`,
styles: [`
:host button {
background-color: var(--button-bg-color);
color: var(--button-text-color);
border: var(--button-border);
}
:host button:hover {
background-color: var(--button-bg-color-hover);
}
`],
})
export class ButtonComponent {
@Input() variant: 'primary' | 'secondary' | 'ghost' = 'primary';
@Input() disabled = false;
get variantClass(): string {
return {
primary: 'bg-primary text-white',
secondary: 'bg-secondary text-white',
ghost: 'bg-transparent text-primary border border-primary',
}[this.variant];
}
}Ce composant ne contient aucune valeur de couleur ou d'espacement hardcodée. Tout provient des tokens, ce qui rend les mises à jour de thème triviales.
Gestion du theming dynamique en Angular
Pour basculer les thèmes à l'exécution, créez un service Angular qui gère l'attribut data-theme et persiste la préférence utilisateur :
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ThemeService {
private currentTheme = 'light';
constructor() {
const saved = localStorage.getItem('theme') || 'light';
this.setTheme(saved);
}
setTheme(theme: 'light' | 'dark'): void {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
this.currentTheme = theme;
}
toggleTheme(): void {
this.setTheme(this.currentTheme === 'light' ? 'dark' : 'light');
}
}Dans votre layout principal, injectez ce service et laissez-le initialiser le thème au démarrage de l'app. Les développeurs n'ont plus besoin de gérer les états visuels : le système de tokens s'en charge.
Pièges courants : cohérence et documentation
Le premier piège est de mélanger des tokens avec des classes Tailwind arbitraires. Vous verrez souvent des développeurs écrire bg-blue-400 directement dans un template au lieu d'utiliser bg-primary . Cela crée des îlots de styles hors contrôle et rend les changements de thème imprévisibles. Solution : appliquez une règle stricte via ESLint ou une convention d'équipe : « seules les classes Tailwind qui consomment des tokens sont autorisées ». Deuxièmement, ne documentez pas vos tokens une fois et oubliez-les. Maintenir un Storybook à jour avec des variantes de thème, des états (hover, disabled, focus) et des cas d'usage réels est non-négociable. Les tokens sans documentation sont juste des variables CSS orphelines.
Conclusion opérationnelle
Un design system Angular + Tailwind solide repose sur trois piliers : des tokens bien structurés, une intégration Tailwind qui consomme ces tokens, et une architecture de composants qui laisse la sémantique au system plutôt qu'aux détails visuels. Commencez par définir vos tokens primitifs et sémantiques dans un fichier CSS unique, configurez Tailwind pour les consommer, puis construisez vos composants Angular sans penser aux couleurs ou espacements. Avec cette discipline, vous obtenez une maintenabilité prédictible, un onboarding développeur plus rapide, et la capacité à pivoter le design complet en quelques secondes.