{% extends 'base.html.twig' %} {% block title %}FormField - Molécules - Design System - Bubul{% endblock %} {% block body %}
{# ===== HEADER ===== #}
Molécules /

FormField

Champ de formulaire complet (Label + Input + Messages)

{# ===== NAVIGATION RAPIDE ===== #} {# ===== CONCEPT MOLÉCULE ===== #}
🧬

Qu'est-ce qu'une Molécule ?

Atomic Design

Une molécule combine plusieurs atomes pour créer un composant fonctionnel réutilisable. Elle encapsule la logique de composition.

Label + Input + Error = FormField

Avantages

  • DRY : Plus besoin de répéter label + input + error
  • Cohérence : Même structure partout
  • Maintenance : Un seul fichier à modifier
{# ===== USAGE ===== #}

Usage

Le composant FormField encapsule un label, un champ de saisie et les messages d'aide/erreur en une seule inclusion. Il gère automatiquement l'accessibilité (for/id).

{% verbatim %}{# Avant : 10+ lignes par champ #}
{% if error %}

{{ error }}

{% endif %}
{# Après avec FormField : 1 inclusion #} {% include '_molecules/form/form-field.html.twig' with { name: 'email', label: 'Adresse email', type: 'email', placeholder: 'votre@email.com', required: true, error: form_errors } %}{% endverbatim %}
Fichier : templates/_molecules/form/form-field.html.twig
{# ===== EXEMPLE FORMULAIRE LOGIN ===== #}

Exemple : Formulaire de connexion

Démonstration de la réutilisabilité

{# Rendu visuel #}

Rendu

{# Email #} {% include '_molecules/form/form-field.html.twig' with { name: 'email', label: 'Adresse email', type: 'email', placeholder: 'votre@email.com', required: true, icon: 'mail' } %} {# Password #} {% include '_molecules/form/form-field.html.twig' with { name: 'password', label: 'Mot de passe', type: 'password', placeholder: 'Votre mot de passe', required: true, icon: 'lock' } %} {# Remember me #} {% include '_molecules/form/form-checkbox.html.twig' with { name: 'remember', label: 'Se souvenir de moi', checked: true } %} {# Submit #}
{% include '_atoms/button/button.html.twig' with { label: 'Se connecter', type: 'submit', size: 'xl', width: 'full', iconPosition: 'left' } %}
{# Code #}

Code

{% verbatim %}
{# Email #} {% include '_molecules/form/form-field.html.twig' with { name: 'email', label: 'Adresse email', type: 'email', placeholder: 'votre@email.com', required: true, icon: 'mail' } %} {# Password #} {% include '_molecules/form/form-field.html.twig' with { name: 'password', label: 'Mot de passe', type: 'password', placeholder: 'Votre mot de passe', required: true, icon: 'lock' } %} {# Remember me #} {% include '_molecules/form/form-checkbox.html.twig' with { name: 'remember', label: 'Se souvenir de moi', checked: true } %} {# Submit #} {% include '_atoms/button/button.html.twig' with { label: 'Se connecter', type: 'submit', width: 'full' } %}
{% endverbatim %}
{# Comparaison avant/après #}

Gain de productivité

Avant : ~50 lignes de HTML répétitif
Après : ~20 lignes avec molécules
{# ===== AVEC ERREURS ===== #}

Exemple avec erreurs

Validation côté serveur

{% include '_molecules/form/form-field.html.twig' with { name: 'email_error', label: 'Adresse email', type: 'email', value: 'test@invalid', required: true, icon: 'mail', error: 'Cette adresse email n\'est pas valide' } %} {% include '_molecules/form/form-field.html.twig' with { name: 'password_error', label: 'Mot de passe', type: 'password', required: true, icon: 'lock', error: 'Le mot de passe doit contenir au moins 8 caractères' } %}
{# ===== VARIANTES ===== #}

Variantes

{# Types d'input #}

Types de champs

{# Text #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_text', label: 'Nom complet', type: 'text', placeholder: 'Jean Dupont' } %}
type: 'text'
{# Email #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_email', label: 'Email', type: 'email', placeholder: 'email@example.com', icon: 'mail' } %}
type: 'email', icon: 'mail'
{# Password #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_password', label: 'Mot de passe', type: 'password', placeholder: '********', icon: 'lock' } %}
type: 'password', icon: 'lock'
{# Search #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_search', label: 'Rechercher', type: 'search', placeholder: 'Rechercher...', icon: 'search', srOnly: true } %}
type: 'search', srOnly: true
{# Textarea #}

Textarea

{% include '_molecules/form/form-field.html.twig' with { name: 'demo_description', label: 'Description', type: 'textarea', placeholder: 'Décrivez votre projet...', rows: 4, hint: 'Maximum 500 caractères' } %}
type: 'textarea', rows: 4
{# File Upload #}

File Upload

{# Simple file input #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_file', label: 'Document', type: 'file', accept: '.pdf,.doc,.docx', hint: 'PDF ou Word, max 20 MB' } %}
type: 'file', accept: '.pdf,.doc'
{# Multiple files #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_files', label: 'Images', type: 'file', accept: '.jpg,.png,.webp', multiple: true, hint: 'Plusieurs fichiers autorisés' } %}
type: 'file', multiple: true
{# Dropzone #}

Dropzone (Drag & Drop)

{# Dropzone simple #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_dropzone', label: 'Document PDF', type: 'file', dropzone: true, accept: '.pdf', maxSize: '20 MB' } %}
dropzone: true, maxSize: '20 MB'
{# Dropzone custom #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_dropzone_custom', label: 'Template PowerPoint', type: 'file', dropzone: true, accept: '.pptx', maxSize: '50 MB', dropzoneText: 'Deposez votre template ici', dropzoneHint: 'Format PPTX uniquement' } %}
dropzoneText: '...', dropzoneHint: '...'
{# Tailles #}

Tailles

{% include '_molecules/form/form-field.html.twig' with { name: 'demo_sm', label: 'Small', placeholder: 'Taille sm', size: 'sm' } %} {% include '_molecules/form/form-field.html.twig' with { name: 'demo_md', label: 'Medium (défaut)', placeholder: 'Taille md', size: 'md' } %} {% include '_molecules/form/form-field.html.twig' with { name: 'demo_lg', label: 'Large', placeholder: 'Taille lg', size: 'lg' } %}
{# ===== ÉTATS ===== #}

États

{# Required #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_required', label: 'Champ requis', required: true, placeholder: 'Obligatoire' } %}
required: true
{# Optional #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_optional', label: 'Champ optionnel', optional: true, placeholder: 'Facultatif' } %}
optional: true
{# Disabled #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_disabled', label: 'Champ désactivé', value: 'Non modifiable', disabled: true } %}
disabled: true
{# With hint #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_hint', label: 'Avec aide', placeholder: 'Tapez ici', hint: 'Ce texte aide l\'utilisateur' } %}
hint: 'Texte d\'aide'
{# With error #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_error', label: 'Avec erreur', value: 'Valeur invalide', error: 'Ce champ contient une erreur' } %}
error: 'Message d\'erreur'
{# With tooltip #}
{% include '_molecules/form/form-field.html.twig' with { name: 'demo_tooltip', label: 'Avec tooltip', placeholder: 'Survolez le (i)', labelTooltip: 'Information supplémentaire' } %}
labelTooltip: 'Info'
{# ===== PARAMÈTRES ===== #}

Référence des paramètres

Paramètre Type Défaut Description
name string '' Nom du champ (requis)
label string null Texte du label
type string 'text' text, email, password, textarea, number, etc.
placeholder string '' Texte placeholder
value string '' Valeur initiale
required bool false Champ obligatoire (*)
optional bool false Affiche "(optionnel)"
disabled bool false Champ désactivé
error string null Message d'erreur
hint string null Texte d'aide
icon string null Nom de l'icône Solar
iconPosition string 'left' left, right
size string 'md' sm, md, lg
variant string 'default' default, filled
srOnly bool false Label masqué (screen readers)
labelTooltip string null Tooltip sur le label
rows number 4 Lignes (textarea only)
Parametres type: file
accept string null Types acceptes (ex: '.pdf,.doc')
multiple bool false Autoriser plusieurs fichiers
dropzone bool false Mode drag & drop
maxSize string null Taille max affichee (ex: '20 MB')
dropzoneText string 'Glissez...' Texte principal dropzone
dropzoneHint string 'ou cliquez...' Texte secondaire dropzone
{% endblock %}