Formulaires

Système de formulaires complet : multi-steps, logique conditionnelle, champs custom, validation, toasts, webhooks.

Structure de base

<form class="form" data-form-webhook="https://votre-webhook.com"
      data-form-redirect="/merci"
      data-form-success="Merci !"
      data-form-error="Erreur, réessayez.">

  <div class="form__field form__field--float">
    <input type="text" name="nom" class="form__input"
           placeholder=" " data-validate="required|min:2">
    <label class="form__label">Nom</label>
  </div>

  <div class="form__nav">
    <button type="submit" class="form__submit">Envoyer</button>
  </div>
</form>
AttributDescription
data-form-webhookURL du webhook (POST JSON)
data-form-redirectURL de redirection après succès
data-form-successMessage toast de succès
data-form-errorMessage toast d'erreur

Labels flottants

Ajoutez form__field--float pour activer l'animation de label flottant. Le label se place dans le champ quand il est vide, et monte en haut quand le champ est focus ou rempli.

Important : l'input doit être placé avant le label dans le HTML, et avoir placeholder=" " (espace).

<!-- Label flottant : input AVANT label, placeholder=" " obligatoire -->
<div class="form__field form__field--float">
  <input type="text" name="nom" class="form__input"
         placeholder=" " data-validate="required|min:2">
  <label class="form__label">Nom complet</label>
</div>

<!-- Textarea flottant -->
<div class="form__field form__field--float">
  <textarea name="message" class="form__textarea"
            placeholder=" "></textarea>
  <label class="form__label">Votre message</label>
</div>

Structure complète d'un champ

<div class="form__field form__field--float">
  <input type="text" name="nom" class="form__input"
         placeholder=" " data-validate="required|min:2">
  <label class="form__label">Nom complet</label>
  <span class="form__label-hint">Optionnel</span>
  <div class="form__error"></div>
</div>
ClasseDescription
form__field--floatActive le label flottant sur le champ
form__label-hintTexte d'indication sous le label (ex: « Optionnel », « Si vous avez déjà un site »)
form__errorConteneur du message d'erreur de validation (rempli automatiquement par JS)
  • Le placeholder=" " (espace) est nécessaire pour le sélecteur CSS :placeholder-shown. Si oublié, le JS l'ajoute automatiquement.
  • L'input doit être avant le label dans le DOM (ordre inversé).
  • Le form__error est optionnel : s'il est présent, le message d'erreur y sera injecté. Sinon, il est créé automatiquement.
  • Compatible avec la validation, les états d'erreur, et le pré-remplissage par URL.
  • Pour les champs custom (select, radio, checkbox), le label classique au-dessus est recommandé.

Multi-Steps

<form class="form" data-form-webhook="...">
  <div class="form__step-indicators"></div>
  <div class="form__progress">
    <div class="form__progress-bar"></div>
  </div>

  <div class="form__step" data-step-label="Infos">
    <!-- Champs step 1 -->
  </div>

  <div class="form__step" data-step-label="Projet">
    <!-- Champs step 2 -->
  </div>

  <div class="form__nav">
    <button type="button" class="form__prev">
      <span data-icon="arrow-left" data-icon-size="16"></span>
      Précédent
    </button>
    <button type="button" class="form__next">
      Suivant
      <span data-icon="arrow-right" data-icon-size="16"></span>
    </button>
    <button type="submit" class="form__submit">
      <span data-icon="paper-airplane" data-icon-size="16"></span>
      Envoyer
    </button>
  </div>
</form>
  • Barre de progression et indicateurs générés automatiquement
  • Validation par step avant de passer au suivant
  • Boutons Précédent/Suivant/Envoyer gérés automatiquement
  • Les icônes dans les boutons héritent de la couleur du texte (currentColor)
  • Le bouton Précédent est à gauche, Suivant et Envoyer sont alignés à droite

Validation

Ajoutez data-validate sur un input avec les règles séparées par | :

RègleDescription
requiredChamp obligatoire
emailFormat email valide
phoneFormat téléphone valide
urlFormat URL (http/https)
min:NMinimum N caractères
max:NMaximum N caractères
<input data-validate="required|email">
<input data-validate="required|min:3|max:50">
<input data-validate="phone">

Logique conditionnelle

Affichez/masquez des champs en fonction de la valeur d'un autre champ :

Particulier
Professionnel
<!-- Champ de référence -->
<div class="form__radio-group" data-name="type">
  <div class="form__radio-option" data-value="pro">Pro</div>
  <div class="form__radio-option" data-value="perso">Perso</div>
</div>

<!-- Visible uniquement si type = pro -->
<div class="form__field" data-condition="type=pro">
  <label class="form__label">Entreprise</label>
  <input name="entreprise" class="form__input">
</div>
FormatDescription
champ=valeurÉgalité
champ!=valeurDifférent
champ=val1,val2L'une des valeurs (OR)
champ=*Non vide
champ>5Supérieur à
champ<5Inférieur à

Les champs masqués ne sont pas envoyés dans le webhook.

Champs custom (sans dépendances)

Des éléments de formulaire 100% stylisables en CSS, remplaçant les éléments natifs difficiles à styler :

Custom Select

Choisir un pays...
France
Belgique
Suisse
<div class="form__select" data-name="pays">
  <div class="form__select-trigger" data-placeholder="Choisir...">Choisir...</div>
  <div class="form__select-options">
    <div class="form__select-option" data-value="fr">France</div>
    <div class="form__select-option" data-value="be">Belgique</div>
    <div class="form__select-option" data-value="ch">Suisse</div>
  </div>
</div>

Custom Number

<div class="form__number">
  <button type="button" class="form__number-minus">−</button>
  <input type="number" name="qty" class="form__number-input"
         value="1" min="1" max="100" step="1">
  <button type="button" class="form__number-plus">+</button>
</div>

Custom Radio Group

S
M
L
<div class="form__radio-group" data-name="taille">
  <div class="form__radio-option" data-value="s">S</div>
  <div class="form__radio-option" data-value="m">M</div>
  <div class="form__radio-option" data-value="l">L</div>
</div>

Custom Checkbox Group

Option 1
Option 2
Option 3
<div class="form__checkbox-group" data-name="options">
  <div class="form__checkbox-option" data-value="opt1">Option 1</div>
  <div class="form__checkbox-option" data-value="opt2">Option 2</div>
  <div class="form__checkbox-option" data-value="opt3">Option 3</div>
</div>

Custom Multi Select

Choisir...
HTML
CSS
JavaScript
<div class="form__multiselect" data-name="competences">
  <div class="form__multiselect-trigger" data-placeholder="Choisir...">Choisir...</div>
  <div class="form__multiselect-options">
    <div class="form__multiselect-option" data-value="html">HTML</div>
    <div class="form__multiselect-option" data-value="css">CSS</div>
    <div class="form__multiselect-option" data-value="js">JavaScript</div>
  </div>
</div>

Webhook (payload)

Proxy sécurisé (recommandé)

En production, utilisez le proxy api/form.php au lieu d'exposer l'URL webhook Make.com dans le HTML :

<form class="form" data-form-webhook="/api/form.php">

Le proxy lit FORM_WEBHOOK_URL et FORM_NOTIFICATION_EMAIL depuis le fichier .env du serveur, ajoute l'email de notification et la date au payload, puis forward vers Make.com. Voir Mise en production → .env pour la configuration.

Création automatique du scénario Make.com

Si vous utilisez Claude Code, la commande « crée le formulaire » crée automatiquement :

  1. Un webhook Make.com (Custom Webhook)
  2. Un scénario : Webhook → Email (envoi des données du formulaire)
  3. L'activation du scénario
  4. La mise à jour du .env avec l'URL du webhook

Prérequis : renseigner MAKE_API_KEY, MAKE_ZONE et MAKE_TEAM_ID dans le fichier .env.

Payload

Le formulaire envoie un POST JSON au webhook configuré. Le payload contient :

  • Tous les champs visibles (respect de la logique conditionnelle)
  • date_now : date française (ex: « 2 Février 2026, 10h52 »)
  • url : URL de la page
  • user_agent : navigateur de l'utilisateur
  • utm_source, utm_medium, utm_campaign, utm_term, utm_content : UTMs depuis l'URL

Exemple de payload

{
  "nom": "Manon",
  "email": "manon@exemple.fr",
  "type_client": "entreprise",
  "entreprise": "Mon Agence",
  "projet": "site-vitrine",
  "services": "design,dev",
  "date_now": "26 Février 2026, 14h30",
  "url": "https://monsite.fr/contact?utm_source=google",
  "user_agent": "Mozilla/5.0...",
  "utm_source": "google",
  "utm_medium": "cpc"
}

Toasts

Les toasts sont automatiques (succès/erreur) mais utilisables aussi manuellement :

// Afficher un toast manuellement
showToast('Message ici', 'success');  // success, error, warning, info
showToast('Attention !', 'warning', 6000);  // durée en ms

Pré-remplissage par URL

Les paramètres d'URL correspondent aux attributs name des champs :

https://monsite.fr/contact?nom=Manon&email=manon@ex.fr&type_client=entreprise

Les inputs natifs, selects, radios et checkboxes sont pré-remplis automatiquement. Les champs custom (data-name) sont également supportés.

Problèmes courants
  • Le formulaire ne s'envoie pas : vérifiez que data-form-webhook pointe vers /api/form.php et que le fichier .env contient FORM_WEBHOOK_URL.
  • La validation ne fonctionne pas : ajoutez l'attribut data-validate sur chaque champ avec les règles séparées par | (ex : data-validate="required|email"). Le formulaire n'a pas besoin d'attribut spécial — le JS détecte automatiquement les champs portant data-validate.
  • Le multi-step ne passe pas à l'étape suivante : chaque étape doit être un <div class="form__step"> et les boutons navigation doivent avoir les classes .form__next / .form__prev.
  • Le champ conditionnel ne s'affiche pas : vérifiez la syntaxe de data-condition : le nom du champ doit correspondre à l'attribut name (pas l'id).
  • Le toast ne s'affiche pas : appelez showToast('message', 'success') depuis la console pour tester. Vérifiez que forms.css est chargé.