Un design system n'est pas une collection de boutons jolis. C'est un contrat entre votre équipe frontend et le reste de l'organisation : des composants testés, documentés, versionés. Avec Angular et Tailwind, vous avez les outils pour le faire proprement, mais les pièges sont nombreux.
La structure : workspace monorepo ou librairie dédiée ?
Deux approches dominent. D'abord, le monorepo avec Nx : vous isolez votre design system dans une librairie @myorg/ui , versionée indépendamment, consommée par plusieurs applications Angular. C'est scalable, mais demande de la discipline : éviter les dépendances circulaires, gérer les peer dependencies avec rigueur. Deuxième approche, une librairie npm publiée séparément, consommée comme dépendance externe. Plus lourde à maintenir en développement local, mais offre une vraie séparation des responsabilités.
Tailwind s'adapte bien aux deux. Dans le monorepo, configurez un tailwind.config.ts unique au niveau de la librairie UI, avec vos tokens de couleur, d'espacement, de typographie. Chaque application consommatrice étend ce config pour ses besoins spécifiques. Ne dupliquez pas les tokens.
Composants : Angular components + Tailwind utilities
Ici, la tentation est d'inliner les classes Tailwind directement dans le template. Mauvaise idée à grande échelle. Préférez des composants réutilisables avec des inputs pour les variantes : <app-button [variant]="'primary'" [size]="'lg'">Submit</app-button> .
Le composant encapsule la logique Tailwind :
@Component({
selector: 'app-button',
template: `<button [ngClass]="buttonClasses"><ng-content></ng-content></button>`
})
export class ButtonComponent {
@Input() variant: 'primary' | 'secondary' = 'primary';
@Input() size: 'sm' | 'md' | 'lg' = 'md';
get buttonClasses(): string {
const baseClasses = 'font-semibold rounded transition';
const variantClasses = this.variant === 'primary' ? 'bg-blue-600 text-white hover:bg-blue-700' : 'bg-gray-200 text-gray-900';
const sizeClasses = this.size === 'lg' ? 'px-6 py-3 text-lg' : 'px-4 py-2';
return `${baseClasses} ${variantClasses} ${sizeClasses}`;
}
} C'est verbeux, mais maintenable. Alternativement, explorez les @apply directives Tailwind, mais avec modération : elles peuvent devenir un anti-pattern si vous les utilisez pour contourner les limitations de composition.
Documentation et Storybook
Un design system sans documentation est du code mort. Storybook est votre ami. Configurez-le avec Angular et Tailwind (l'intégration est directe), puis documentez chaque composant avec des stories interactives. Montrez les variantes, les états (focus, disabled, loading), les cas limites.
import { Meta, StoryObj } from '@storybook/angular';
import { ButtonComponent } from './button.component';
const meta: Meta<ButtonComponent> = {
component: ButtonComponent,
title: 'Components/Button'
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
args: { variant: 'primary', size: 'md' }
};
export const Secondary: Story = {
args: { variant: 'secondary', size: 'lg' }
};Versionning et consommation
Publiez votre librairie avec un strict semver. Si vous modifiez l'API d'un composant (renommage d'input, suppression d'une variante), c'est une breaking change. Documentez les migrations. Vos consommateurs doivent pouvoir upgrader en confiance.
Dans vos applications, pincez la version : @myorg/ui@~1.5.0 . Testez les nouvelles versions en staging avant production. Les design systems, c'est de la dette technique partagée : négliger les mises à jour crée de la fragmentation.
Performance et bundle size
Tailwind génère beaucoup de CSS. Avec PurgeCSS (inclus par défaut), seules les classes utilisées sont incluses. Mais si vous publiez une librairie, assurez-vous que le consommateur purge aussi sur son propre code. Documentez cela clairement.
Pour les icônes, intégrez une librairie légère comme Feather Icons ou Material Icons, pas tout le set. Lazy-load les composants lourds (modals, datepickers) si possible.
Un design system bien conçu économise des semaines de développement. Le coût initial est réel, mais l'amortissement est rapide dès que deux ou trois applications le consomment.