# 🎨 Guide des Bonnes Pratiques - Composants Frontend > **Guide**: Design System Notytex > **Audience**: Développeurs Frontend & UI/UX Designers > **Version**: 1.0 > **Mise à jour**: 7 août 2025 ## 🎯 **Philosophie du Design System** Notytex adopte une approche **moderne, cohérente et accessible** pour ses composants frontend. Chaque composant doit être **réutilisable**, **maintenable** et **évolutif**. ### 🏗️ **Principes Fondamentaux** - **🔄 Réutilisabilité** : Un composant = plusieurs contextes - **🎨 Cohérence** : Design tokens centralisés - **📱 Responsive-first** : Mobile d'abord - **♿ Accessibilité** : WCAG 2.1 AA minimum - **⚡ Performance** : Optimisation native --- ## 📁 **Architecture des Composants** ### **Structure de Répertoires** ``` templates/components/ ├── common/ │ ├── macros.html # Macros réutilisables │ └── base_components.html # Composants de base ├── assessment/ │ └── assessment_card.html # Cartes d'évaluation ├── class/ │ └── class_card.html # Cartes de classe ├── forms/ │ ├── form_inputs.html # Champs de formulaire │ └── form_validation.html # Validation UI └── ui/ ├── buttons.html # Système de boutons ├── modals.html # Modales └── notifications.html # Toasts/Alerts ``` ### **Convention de Nommage** ``` Type_Domain_Component.html │ │ │ │ │ └─ Nom spécifique (card, button, modal...) │ └──────── Domaine métier (assessment, class, user...) └───────────── Type (component, macro, layout...) ``` **📝 Exemples:** - `assessment_card.html` ✅ - `class_list_item.html` ✅ - `user_profile_widget.html` ✅ - `genericCard.html` ❌ (PascalCase) - `card.html` ❌ (trop générique) --- ## 🎨 **Design Tokens & Cohérence** ### **Palette de Couleurs Système** ```scss // Couleurs Primaires $blue: from-blue-500 to-blue-600 // Actions principales $green: from-green-500 to-green-600 // Succès, validation $purple: from-purple-500 to-purple-600 // Évaluations, analytique $orange: from-orange-500 to-orange-600 // Avertissement, en cours $red: from-red-500 to-red-600 // Erreur, critique $pink: from-pink-500 to-pink-600 // Spécial, mise en avant // Couleurs Neutres $gray-50: #f9fafb // Fond très clair $gray-100: #f3f4f6 // Fond clair $gray-600: #4b5563 // Texte secondaire $gray-900: #111827 // Texte principal ``` ### **Typography Scale** ```scss // Hiérarchie Textuelle .text-4xl { font-size: 2.25rem } // Titres Hero .text-3xl { font-size: 1.875rem } // Titres principaux .text-2xl { font-size: 1.5rem } // Titres sections .text-xl { font-size: 1.25rem } // Sous-titres .text-lg { font-size: 1.125rem } // Texte large .text-base { font-size: 1rem } // Texte standard .text-sm { font-size: 0.875rem } // Texte petit .text-xs { font-size: 0.75rem } // Métadonnées ``` ### **Spacing System** ```scss // Système d'espacement (Tailwind) space-1: 0.25rem // 4px space-2: 0.5rem // 8px space-3: 0.75rem // 12px space-4: 1rem // 16px space-6: 1.5rem // 24px space-8: 2rem // 32px space-12: 3rem // 48px ``` --- ## 🧩 **Anatomie d'un Composant** ### **Template Structure** ```jinja2 {# 1. Commentaire de description #} {# Composant pour [FONCTION] dans [CONTEXTE] #} {# 2. Import des dépendances #} {% from 'components/common/macros.html' import helper_macro %} {# 3. Définition du macro principal #} {% macro component_name(required_param, optional_param=default) %} {# 4. Logique de configuration #} {% set config = { 'variant': optional_param, 'classes': 'base-classes ' + (additional_classes if condition else '') } %} {# 5. Structure HTML avec données dynamiques #}
{% if caller %} {{ caller() }} {% else %} {% endif %}
{# 6. Fin du macro #} {% endmacro %} ``` ### **Exemple Concret : Button Component** ```jinja2 {# Composant bouton avec variants et états #} {% macro button( text, url=None, type="primary", size="md", icon=None, disabled=False, classes="" ) %} {# Configuration des variants #} {% set variants = { 'primary': 'bg-gradient-to-r from-blue-500 to-blue-600 text-white', 'secondary': 'bg-gray-50 text-gray-700 border border-gray-300', 'danger': 'bg-gradient-to-r from-red-500 to-red-600 text-white' } %} {% set sizes = { 'sm': 'px-3 py-2 text-sm', 'md': 'px-4 py-2 text-base', 'lg': 'px-6 py-3 text-lg' } %} {# Classes finales #} {% set button_classes = [ 'inline-flex items-center justify-center', 'rounded-xl font-semibold transition-all duration-300', 'hover:shadow-lg transform hover:scale-105', variants[type], sizes[size], 'opacity-50 cursor-not-allowed' if disabled else '', classes ]|join(' ') %} {# Rendu conditionnel (lien vs bouton) #} {% if url and not disabled %} {% else %} {% endif %} {% endmacro %} ``` --- ## 🎛️ **API des Composants** ### **Paramètres Standards** Chaque composant doit exposer une API cohérente : ```jinja2 {% macro component_name( # === OBLIGATOIRES === data, # Données principales title, # Titre/Label # === OPTIONNELS === variant="default", # Type/Style (primary, secondary, danger...) size="md", # Taille (sm, md, lg, xl) classes="", # Classes CSS additionnelles # === COMPORTEMENT === clickable=True, # Interactif ou non disabled=False, # État désactivé loading=False, # État de chargement # === CONTENU === icon=None, # Icône SVG badge=None, # Badge/Compteur actions=[], # Liste d'actions # === TECHNIQUE === id=None, # ID HTML personnalisé attrs={} # Attributs HTML additionnels ) %} ``` ### **Exemple d'Utilisation** ```jinja2 {# Usage simple #} {{ button("Sauvegarder") }} {# Usage avancé #} {{ button( text="Créer évaluation", url=url_for('assessments.new'), type="primary", size="lg", icon='...', classes="w-full md:w-auto" ) }} ``` --- ## 🔄 **États et Variants** ### **États Standardisés** Tous les composants interactifs doivent gérer ces états : ```scss // États de base .default // État normal .hover // Survol .active // Actif/Pressé .focus // Focus clavier .disabled // Désactivé // États applicatifs .loading // Chargement en cours .success // Succès/Validation .error // Erreur .warning // Avertissement ``` ### **Variants de Style** ```scss // Variants visuels .primary // Action principale (bleu) .secondary // Action secondaire (gris) .success // Succès (vert) .warning // Attention (orange) .danger // Danger (rouge) .ghost // Transparent .outline // Contour seulement ``` ### **Exemple : Card States** ```jinja2 {# États d'une carte #} {% set card_states = { 'default': 'bg-white shadow-lg hover:shadow-xl', 'active': 'bg-blue-50 border-2 border-blue-200 shadow-xl', 'disabled': 'bg-gray-50 opacity-50 cursor-not-allowed', 'loading': 'bg-gray-50 animate-pulse' } %}
{% if state == 'loading' %}
{% else %} {% endif %}
``` --- ## 📱 **Responsive Design** ### **Breakpoints Standard** ```scss // Mobile First approach sm: 640px // Téléphone large / Tablette portrait md: 768px // Tablette lg: 1024px // Desktop xl: 1280px // Large desktop 2xl: 1536px // Ultra wide ``` ### **Grilles Responsives** ```jinja2 {# Grille adaptative standard #}
{% for item in items %} {{ item_card(item) }} {% endfor %}
{# Navigation responsive #} ``` ### **Typography Responsive** ```scss // Échelle typographique adaptative .hero-title { @apply text-2xl md:text-4xl lg:text-5xl; } .section-title { @apply text-lg md:text-xl lg:text-2xl; } .body-text { @apply text-sm md:text-base; } ``` --- ## ♿ **Accessibilité** ### **Checklist Obligatoire** - [ ] **Contraste** ≥ 4.5:1 pour texte normal - [ ] **Contraste** ≥ 3:1 pour texte large - [ ] **Navigation clavier** complète - [ ] **ARIA labels** sur éléments interactifs - [ ] **Focus indicators** visibles - [ ] **Screen reader** compatible ### **Implémentation** ```jinja2 {# Bouton accessible #} {# Card interactive accessible #}
``` ### **ARIA Patterns** ```html