veza/veza-docs/vision/domains/frontend/design-system.md

26 KiB

id title sidebar_label
design-system 🎨 Design System Veza Platform 🎨 Design System Veza Platform

NOTE: Cette page décrit la CIBLE (but visé).

🎨 Design System Veza Platform

Système de design unifié pour toutes les interfaces Veza (Web, Desktop, Mobile)

🎯 Vision & Identité

Identité de Veza

Veza Platform est une plateforme musicale collaborative moderne qui combine :

  • Créativité - Outils de création musicale avancés
  • Collaboration - Jam sessions temps réel et projets partagés
  • Innovation - IA, streaming adaptatif, marketplace
  • Accessibilité - Interface intuitive pour tous les niveaux

Ton Visuel

  • Moderne - Design épuré et contemporain
  • Professionnel - Interface sérieuse pour créateurs
  • Créatif - Couleurs vibrantes et animations fluides
  • Inclusif - Accessible et adaptatif

Inspiration

  • Spotify - Interface audio moderne et intuitive
  • Discord - Collaboration temps réel et communauté
  • SoundCloud - Découverte et partage musical
  • Figma - Outils créatifs professionnels

🎨 Palette de Couleurs

Mode Sombre (Principal)

Couleurs Principales

/* Primary - Bleu/Violet créatif */
--veza-primary-50: #f0f9ff;
--veza-primary-100: #e0f2fe;
--veza-primary-200: #bae6fd;
--veza-primary-300: #7dd3fc;
--veza-primary-400: #38bdf8;
--veza-primary-500: #0ea5e9;  /* Couleur principale */
--veza-primary-600: #0284c7;
--veza-primary-700: #0369a1;
--veza-primary-800: #075985;
--veza-primary-900: #0c4a6e;

/* Secondary - Violet/Purple créatif */
--veza-secondary-50: #fdf4ff;
--veza-secondary-100: #fae8ff;
--veza-secondary-200: #f5d0fe;
--veza-secondary-300: #f0abfc;
--veza-secondary-400: #e879f9;
--veza-secondary-500: #d946ef;  /* Couleur secondaire */
--veza-secondary-600: #c026d3;
--veza-secondary-700: #a21caf;
--veza-secondary-800: #86198f;
--veza-secondary-900: #701a75;

/* Accent - Orange/Ambre énergique */
--veza-accent-50: #fff7ed;
--veza-accent-100: #ffedd5;
--veza-accent-200: #fed7aa;
--veza-accent-300: #fdba74;
--veza-accent-400: #fb923c;
--veza-accent-500: #f97316;  /* Couleur d'accent */
--veza-accent-600: #ea580c;
--veza-accent-700: #c2410c;
--veza-accent-800: #9a3412;
--veza-accent-900: #7c2d12;

Couleurs de Fond

/* Background - Gradients sombres */
--veza-bg-primary: #0f0f23;      /* Fond principal */
--veza-bg-secondary: #1a1a2e;    /* Fond secondaire */
--veza-bg-tertiary: #16213e;     /* Fond tertiaire */
--veza-bg-surface: #1f2937;      /* Surfaces (cards) */
--veza-bg-elevated: #374151;     /* Surfaces élevées */
--veza-bg-overlay: rgba(0, 0, 0, 0.5); /* Overlays */

/* Gradients de fond */
--veza-gradient-primary: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%);
--veza-gradient-header: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--veza-gradient-card: linear-gradient(135deg, #1f2937 0%, #374151 100%);

Couleurs de Texte

/* Text - Hiérarchie claire */
--veza-text-primary: #ffffff;     /* Texte principal */
--veza-text-secondary: #e5e7eb;   /* Texte secondaire */
--veza-text-tertiary: #9ca3af;    /* Texte tertiaire */
--veza-text-muted: #6b7280;       /* Texte atténué */
--veza-text-inverse: #111827;     /* Texte inversé */

/* Text avec opacité */
--veza-text-primary-87: rgba(255, 255, 255, 0.87);
--veza-text-secondary-70: rgba(229, 231, 235, 0.7);

Couleurs d'État

/* Success - Vert */
--veza-success-50: #f0fdf4;
--veza-success-500: #10b981;
--veza-success-600: #059669;
--veza-success-700: #047857;

/* Warning - Jaune */
--veza-warning-50: #fffbeb;
--veza-warning-500: #f59e0b;
--veza-warning-600: #d97706;
--veza-warning-700: #b45309;

/* Error - Rouge */
--veza-error-50: #fef2f2;
--veza-error-500: #ef4444;
--veza-error-600: #dc2626;
--veza-error-700: #b91c1c;

/* Info - Bleu */
--veza-info-50: #eff6ff;
--veza-info-500: #3b82f6;
--veza-info-600: #2563eb;
--veza-info-700: #1d4ed8;

Mode Clair (Alternative)

Couleurs Principales (Clair)

/* Background - Clair */
--veza-light-bg-primary: #ffffff;
--veza-light-bg-secondary: #f9fafb;
--veza-light-bg-tertiary: #f3f4f6;
--veza-light-bg-surface: #ffffff;
--veza-light-bg-elevated: #f9fafb;

/* Text - Clair */
--veza-light-text-primary: #111827;
--veza-light-text-secondary: #374151;
--veza-light-text-tertiary: #6b7280;
--veza-light-text-muted: #9ca3af;

🖋 Typographie

Police Principale

/* Inter - Police moderne et lisible */
--veza-font-family: 'Inter', system-ui, -apple-system, sans-serif;
--veza-font-mono: 'JetBrains Mono', 'Fira Code', monospace;

Hiérarchie Typographique

Titres

/* H1 - Titre principal */
--veza-h1-font-size: 3rem;        /* 48px */
--veza-h1-font-weight: 700;
--veza-h1-line-height: 1.2;
--veza-h1-letter-spacing: -0.025em;

/* H2 - Titre de section */
--veza-h2-font-size: 2.25rem;     /* 36px */
--veza-h2-font-weight: 600;
--veza-h2-line-height: 1.3;
--veza-h2-letter-spacing: -0.025em;

/* H3 - Titre de sous-section */
--veza-h3-font-size: 1.875rem;    /* 30px */
--veza-h3-font-weight: 600;
--veza-h3-line-height: 1.4;

/* H4 - Titre de carte */
--veza-h4-font-size: 1.5rem;      /* 24px */
--veza-h4-font-weight: 600;
--veza-h4-line-height: 1.4;

/* H5 - Titre de composant */
--veza-h5-font-size: 1.25rem;     /* 20px */
--veza-h5-font-weight: 600;
--veza-h5-line-height: 1.5;

/* H6 - Titre de détail */
--veza-h6-font-size: 1.125rem;    /* 18px */
--veza-h6-font-weight: 600;
--veza-h6-line-height: 1.5;

Corps de Texte

/* Body - Texte principal */
--veza-body-font-size: 1rem;      /* 16px */
--veza-body-font-weight: 400;
--veza-body-line-height: 1.6;

/* Body Large - Texte important */
--veza-body-large-font-size: 1.125rem;  /* 18px */
--veza-body-large-font-weight: 400;
--veza-body-large-line-height: 1.6;

/* Body Small - Texte secondaire */
--veza-body-small-font-size: 0.875rem;  /* 14px */
--veza-body-small-font-weight: 400;
--veza-body-small-line-height: 1.5;

/* Caption - Légendes */
--veza-caption-font-size: 0.75rem;       /* 12px */
--veza-caption-font-weight: 400;
--veza-caption-line-height: 1.4;

Interface

/* Button - Boutons */
--veza-button-font-size: 0.875rem;       /* 14px */
--veza-button-font-weight: 500;
--veza-button-line-height: 1.4;

/* Input - Champs de saisie */
--veza-input-font-size: 0.875rem;        /* 14px */
--veza-input-font-weight: 400;
--veza-input-line-height: 1.4;

/* Label - Étiquettes */
--veza-label-font-size: 0.875rem;        /* 14px */
--veza-label-font-weight: 500;
--veza-label-line-height: 1.4;

🔲 Composants UI de Base

Boutons

Variantes de Boutons

// Primary Button
<button className="bg-veza-primary-500 hover:bg-veza-primary-600 text-white px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-veza-primary-500 focus:ring-offset-2">
  Action Principale
</button>

// Secondary Button
<button className="bg-veza-secondary-500 hover:bg-veza-secondary-600 text-white px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-veza-secondary-500 focus:ring-offset-2">
  Action Secondaire
</button>

// Ghost Button
<button className="text-veza-primary-500 hover:text-veza-primary-400 hover:bg-veza-primary-50 px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-veza-primary-500 focus:ring-offset-2">
  Action Ghost
</button>

// Danger Button
<button className="bg-veza-error-500 hover:bg-veza-error-600 text-white px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-veza-error-500 focus:ring-offset-2">
  Action Dangereuse
</button>

Tailles de Boutons

// Small
<button className="px-3 py-1.5 text-sm">Petit</button>

// Medium (default)
<button className="px-4 py-2 text-sm">Moyen</button>

// Large
<button className="px-6 py-3 text-base">Grand</button>

// Extra Large
<button className="px-8 py-4 text-lg">Très Grand</button>

Inputs

Champs de Texte

// Text Input
<input 
  type="text"
  className="w-full px-3 py-2 bg-veza-bg-surface border border-veza-bg-elevated rounded-lg text-veza-text-primary placeholder-veza-text-muted focus:outline-none focus:ring-2 focus:ring-veza-primary-500 focus:border-veza-primary-500 transition-colors duration-200"
  placeholder="Entrez votre texte..."
/>

// Password Input
<input 
  type="password"
  className="w-full px-3 py-2 bg-veza-bg-surface border border-veza-bg-elevated rounded-lg text-veza-text-primary placeholder-veza-text-muted focus:outline-none focus:ring-2 focus:ring-veza-primary-500 focus:border-veza-primary-500 transition-colors duration-200"
  placeholder="Mot de passe..."
/>

// Search Input
<div className="relative">
  <input 
    type="search"
    className="w-full pl-10 pr-3 py-2 bg-veza-bg-surface border border-veza-bg-elevated rounded-lg text-veza-text-primary placeholder-veza-text-muted focus:outline-none focus:ring-2 focus:ring-veza-primary-500 focus:border-veza-primary-500 transition-colors duration-200"
    placeholder="Rechercher..."
  />
  <svg className="absolute left-3 top-2.5 h-5 w-5 text-veza-text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
  </svg>
</div>

États des Inputs

// Error State
<input 
  className="w-full px-3 py-2 bg-veza-bg-surface border border-veza-error-500 rounded-lg text-veza-text-primary focus:outline-none focus:ring-2 focus:ring-veza-error-500 transition-colors duration-200"
/>

// Success State
<input 
  className="w-full px-3 py-2 bg-veza-bg-surface border border-veza-success-500 rounded-lg text-veza-text-primary focus:outline-none focus:ring-2 focus:ring-veza-success-500 transition-colors duration-200"
/>

// Disabled State
<input 
  disabled
  className="w-full px-3 py-2 bg-veza-bg-tertiary border border-veza-bg-elevated rounded-lg text-veza-text-muted cursor-not-allowed"
/>

Cards

Card de Base

// Basic Card
<div className="bg-veza-bg-surface border border-veza-bg-elevated rounded-xl p-6 shadow-lg hover:shadow-xl transition-shadow duration-200">
  <h3 className="text-veza-h4-font-size font-veza-h4-font-weight text-veza-text-primary mb-2">
    Titre de la Carte
  </h3>
  <p className="text-veza-body-font-size text-veza-text-secondary">
    Contenu de la carte avec description détaillée.
  </p>
</div>

// Interactive Card
<div className="bg-veza-bg-surface border border-veza-bg-elevated rounded-xl p-6 shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200 cursor-pointer">
  <div className="flex items-center justify-between mb-4">
    <h3 className="text-veza-h4-font-size font-veza-h4-font-weight text-veza-text-primary">
      Carte Interactive
    </h3>
    <span className="text-veza-accent-500"></span>
  </div>
  <p className="text-veza-body-font-size text-veza-text-secondary">
    Cliquez pour plus d'informations.
  </p>
</div>

Card avec Gradient

// Gradient Card
<div className="bg-gradient-to-br from-veza-primary-500 to-veza-secondary-500 rounded-xl p-6 text-white shadow-lg">
  <h3 className="text-veza-h4-font-size font-veza-h4-font-weight mb-2">
    Carte avec Gradient
  </h3>
  <p className="text-white/90">
    Carte avec dégradé de couleurs Veza.
  </p>
</div>

Navigation

Sidebar

// Sidebar Container
<aside className="w-64 bg-veza-bg-secondary border-r border-veza-bg-elevated h-screen">
  <div className="p-6">
    <h2 className="text-veza-h5-font-size font-veza-h5-font-weight text-veza-text-primary mb-6">
      Navigation
    </h2>
    
    {/* Navigation Items */}
    <nav className="space-y-2">
      <a href="/dashboard" className="flex items-center space-x-3 px-4 py-3 rounded-lg text-veza-text-secondary hover:text-veza-text-primary hover:bg-veza-bg-surface transition-colors duration-200">
        <span className="text-xl">📊</span>
        <span className="font-medium">Dashboard</span>
      </a>
      
      <a href="/media" className="flex items-center space-x-3 px-4 py-3 rounded-lg text-veza-text-secondary hover:text-veza-text-primary hover:bg-veza-bg-surface transition-colors duration-200">
        <span className="text-xl">🎵</span>
        <span className="font-medium">Media</span>
      </a>
    </nav>
  </div>
</aside>

Top Navigation

// Top Navigation
<header className="bg-veza-bg-surface border-b border-veza-bg-elevated px-6 py-4">
  <div className="flex items-center justify-between">
    <div className="flex items-center space-x-3">
      <div className="text-2xl">🎵</div>
      <h1 className="text-veza-h5-font-size font-veza-h5-font-weight text-veza-text-primary">
        Veza Platform
      </h1>
    </div>
    
    <nav className="flex items-center space-x-6">
      <a href="/notifications" className="text-veza-text-secondary hover:text-veza-text-primary transition-colors duration-200">
        🔔
      </a>
      <a href="/profile" className="text-veza-text-secondary hover:text-veza-text-primary transition-colors duration-200">
        👤
      </a>
    </nav>
  </div>
</header>

États Interactifs

Hover States

/* Hover sur les boutons */
.button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

/* Hover sur les cards */
.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}

/* Hover sur les liens */
.link:hover {
  color: var(--veza-primary-400);
  text-decoration: underline;
}

Focus States

/* Focus sur les inputs */
.input:focus {
  outline: none;
  ring: 2px;
  ring-color: var(--veza-primary-500);
  ring-offset: 2px;
}

/* Focus sur les boutons */
.button:focus {
  outline: none;
  ring: 2px;
  ring-color: var(--veza-primary-500);
  ring-offset: 2px;
}

Loading States

// Loading Button
<button className="bg-veza-primary-500 text-white px-4 py-2 rounded-lg font-medium opacity-75 cursor-not-allowed">
  <svg className="animate-spin -ml-1 mr-2 h-4 w-4 inline" fill="none" viewBox="0 0 24 24">
    <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
    <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
  </svg>
  Chargement...
</button>

// Loading Spinner
<div className="flex items-center justify-center">
  <svg className="animate-spin h-8 w-8 text-veza-primary-500" fill="none" viewBox="0 0 24 24">
    <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
    <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
  </svg>
</div>

🌗 Modes Clair et Sombre

Variables CSS pour les Thèmes

/* Mode Sombre (Par défaut) */
[data-theme="dark"] {
  --veza-bg-primary: #0f0f23;
  --veza-bg-secondary: #1a1a2e;
  --veza-bg-surface: #1f2937;
  --veza-text-primary: #ffffff;
  --veza-text-secondary: #e5e7eb;
  --veza-border-color: #374151;
}

/* Mode Clair */
[data-theme="light"] {
  --veza-bg-primary: #ffffff;
  --veza-bg-secondary: #f9fafb;
  --veza-bg-surface: #ffffff;
  --veza-text-primary: #111827;
  --veza-text-secondary: #374151;
  --veza-border-color: #e5e7eb;
}

Composant de Toggle de Thème

// Theme Toggle Component
const ThemeToggle: React.FC = () => {
  const [theme, setTheme] = useState<'light' | 'dark'>('dark');

  const toggleTheme = () => {
    const newTheme = theme === 'dark' ? 'light' : 'dark';
    setTheme(newTheme);
    document.documentElement.setAttribute('data-theme', newTheme);
  };

  return (
    <button
      onClick={toggleTheme}
      className="p-2 rounded-lg bg-veza-bg-surface border border-veza-border-color text-veza-text-primary hover:bg-veza-bg-elevated transition-colors duration-200"
    >
      {theme === 'dark' ? '☀️' : '🌙'}
    </button>
  );
};

🎛 Micro-interactions et Animations

Animations CSS

/* Fade In */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in {
  animation: fadeIn 0.5s ease-out;
}

/* Pulse */
@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

.pulse {
  animation: pulse 2s infinite;
}

/* Wave */
@keyframes wave {
  0%, 100% {
    transform: rotate(0deg);
  }
  25% {
    transform: rotate(20deg);
  }
  75% {
    transform: rotate(-20deg);
  }
}

.wave {
  animation: wave 1.5s ease-in-out infinite;
}

/* Slide In */
@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.slide-in {
  animation: slideIn 0.3s ease-out;
}

Transitions

/* Transitions fluides */
* {
  transition: all 0.2s ease-in-out;
}

/* Transitions spécifiques */
.button {
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.card {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.input {
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

Feedback Visuel

// Success Feedback
const SuccessFeedback: React.FC = () => (
  <div className="fixed top-4 right-4 bg-veza-success-500 text-white px-6 py-3 rounded-lg shadow-lg slide-in">
    <div className="flex items-center space-x-2">
      <span></span>
      <span>Action réussie !</span>
    </div>
  </div>
);

// Error Feedback
const ErrorFeedback: React.FC = () => (
  <div className="fixed top-4 right-4 bg-veza-error-500 text-white px-6 py-3 rounded-lg shadow-lg slide-in">
    <div className="flex items-center space-x-2">
      <span></span>
      <span>Une erreur s'est produite</span>
    </div>
  </div>
);

// Loading Feedback
const LoadingFeedback: React.FC = () => (
  <div className="fixed inset-0 bg-veza-bg-overlay flex items-center justify-center">
    <div className="bg-veza-bg-surface rounded-lg p-6 shadow-xl">
      <div className="flex items-center space-x-3">
        <svg className="animate-spin h-6 w-6 text-veza-primary-500" fill="none" viewBox="0 0 24 24">
          <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
          <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
        </svg>
        <span className="text-veza-text-primary">Chargement...</span>
      </div>
    </div>
  </div>
);

📦 Kit de Composants React/Tailwind

Composant Button

// Button.tsx
interface ButtonProps {
  children: React.ReactNode;
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
  size?: 'sm' | 'md' | 'lg' | 'xl';
  disabled?: boolean;
  loading?: boolean;
  onClick?: () => void;
  type?: 'button' | 'submit' | 'reset';
  className?: string;
}

const Button: React.FC<ButtonProps> = ({
  children,
  variant = 'primary',
  size = 'md',
  disabled = false,
  loading = false,
  onClick,
  type = 'button',
  className = '',
}) => {
  const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
  
  const variantClasses = {
    primary: 'bg-veza-primary-500 hover:bg-veza-primary-600 text-white focus:ring-veza-primary-500 hover:scale-105',
    secondary: 'bg-veza-secondary-500 hover:bg-veza-secondary-600 text-white focus:ring-veza-secondary-500 hover:scale-105',
    ghost: 'text-veza-primary-500 hover:text-veza-primary-400 hover:bg-veza-primary-50 focus:ring-veza-primary-500',
    danger: 'bg-veza-error-500 hover:bg-veza-error-600 text-white focus:ring-veza-error-500 hover:scale-105',
  };
  
  const sizeClasses = {
    sm: 'px-3 py-1.5 text-sm',
    md: 'px-4 py-2 text-sm',
    lg: 'px-6 py-3 text-base',
    xl: 'px-8 py-4 text-lg',
  };
  
  const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed hover:scale-100' : '';
  const loadingClasses = loading ? 'cursor-wait' : '';
  
  const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${disabledClasses} ${loadingClasses} ${className}`;
  
  return (
    <button
      type={type}
      className={classes}
      disabled={disabled || loading}
      onClick={onClick}
    >
      {loading && (
        <svg className="animate-spin -ml-1 mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24">
          <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
          <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
        </svg>
      )}
      {children}
    </button>
  );
};

export default Button;

Composant Input

// Input.tsx
interface InputProps {
  type?: 'text' | 'password' | 'email' | 'search' | 'number';
  placeholder?: string;
  value?: string;
  onChange?: (value: string) => void;
  error?: string;
  success?: boolean;
  disabled?: boolean;
  className?: string;
  icon?: React.ReactNode;
}

const Input: React.FC<InputProps> = ({
  type = 'text',
  placeholder,
  value,
  onChange,
  error,
  success,
  disabled = false,
  className = '',
  icon,
}) => {
  const baseClasses = 'w-full px-3 py-2 bg-veza-bg-surface border rounded-lg text-veza-text-primary placeholder-veza-text-muted focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-200';
  
  const stateClasses = error 
    ? 'border-veza-error-500 focus:ring-veza-error-500' 
    : success 
    ? 'border-veza-success-500 focus:ring-veza-success-500'
    : 'border-veza-bg-elevated focus:ring-veza-primary-500 focus:border-veza-primary-500';
  
  const disabledClasses = disabled ? 'bg-veza-bg-tertiary cursor-not-allowed' : '';
  
  const classes = `${baseClasses} ${stateClasses} ${disabledClasses} ${className}`;
  
  return (
    <div className="relative">
      {icon && (
        <div className="absolute left-3 top-2.5 text-veza-text-muted">
          {icon}
        </div>
      )}
      <input
        type={type}
        placeholder={placeholder}
        value={value}
        onChange={(e) => onChange?.(e.target.value)}
        disabled={disabled}
        className={classes}
        style={icon ? { paddingLeft: '2.5rem' } : {}}
      />
      {error && (
        <p className="mt-1 text-sm text-veza-error-500">{error}</p>
      )}
    </div>
  );
};

export default Input;

Composant Card

// Card.tsx
interface CardProps {
  children: React.ReactNode;
  title?: string;
  subtitle?: string;
  interactive?: boolean;
  gradient?: boolean;
  className?: string;
  onClick?: () => void;
}

const Card: React.FC<CardProps> = ({
  children,
  title,
  subtitle,
  interactive = false,
  gradient = false,
  className = '',
  onClick,
}) => {
  const baseClasses = 'rounded-xl p-6 shadow-lg transition-all duration-200';
  
  const styleClasses = gradient
    ? 'bg-gradient-to-br from-veza-primary-500 to-veza-secondary-500 text-white'
    : 'bg-veza-bg-surface border border-veza-bg-elevated';
  
  const interactiveClasses = interactive
    ? 'hover:shadow-xl hover:scale-105 cursor-pointer'
    : '';
  
  const classes = `${baseClasses} ${styleClasses} ${interactiveClasses} ${className}`;
  
  return (
    <div className={classes} onClick={onClick}>
      {(title || subtitle) && (
        <div className="mb-4">
          {title && (
            <h3 className="text-veza-h4-font-size font-veza-h4-font-weight text-veza-text-primary mb-1">
              {title}
            </h3>
          )}
          {subtitle && (
            <p className="text-veza-body-small-font-size text-veza-text-secondary">
              {subtitle}
            </p>
          )}
        </div>
      )}
      {children}
    </div>
  );
};

export default Card;

Composant Modal

// Modal.tsx
interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  title?: string;
  children: React.ReactNode;
  size?: 'sm' | 'md' | 'lg' | 'xl';
}

const Modal: React.FC<ModalProps> = ({
  isOpen,
  onClose,
  title,
  children,
  size = 'md',
}) => {
  if (!isOpen) return null;

  const sizeClasses = {
    sm: 'max-w-md',
    md: 'max-w-lg',
    lg: 'max-w-2xl',
    xl: 'max-w-4xl',
  };

  return (
    <div className="fixed inset-0 bg-veza-bg-overlay flex items-center justify-center z-50">
      <div className={`bg-veza-bg-surface rounded-xl shadow-2xl ${sizeClasses[size]} w-full mx-4`}>
        {title && (
          <div className="flex items-center justify-between p-6 border-b border-veza-bg-elevated">
            <h2 className="text-veza-h4-font-size font-veza-h4-font-weight text-veza-text-primary">
              {title}
            </h2>
            <button
              onClick={onClose}
              className="text-veza-text-muted hover:text-veza-text-primary transition-colors duration-200"
            >
              
            </button>
          </div>
        )}
        <div className="p-6">
          {children}
        </div>
      </div>
    </div>
  );
};

export default Modal;

🎯 Guide d'Utilisation

Principes de Design

  1. Cohérence - Utilisez toujours les mêmes couleurs, typographies et espacements
  2. Accessibilité - Respectez les contrastes et les états de focus
  3. Performance - Optimisez les animations et transitions
  4. Responsive - Adaptez l'interface à tous les écrans
  5. Feedback - Donnez toujours un retour visuel aux actions utilisateur

Checklist d'Implémentation

  • Variables CSS définies dans le thème
  • Composants React créés et documentés
  • Animations CSS optimisées
  • Tests d'accessibilité passés
  • Tests de performance validés
  • Documentation des composants mise à jour

Prochaines Étapes

  1. Implémenter les composants dans les applications existantes
  2. Créer un Storybook pour documenter tous les composants
  3. Tester l'accessibilité et la performance
  4. Former l'équipe sur l'utilisation du design system
  5. Maintenir et évoluer le design system

Dernière mise à jour : $(date) Version : 1.0.0 Statut : Design System Complet