Curated Form Systems

Des formulaires penses comme de vrais écrans produit

Cette page montre comment passer d'un simple composant a une expérience plus premium: confiance, lisibilité, rythme visuel et états UI qui rassurent à chaque étape.

Auth Connexion, inscription et signaux de confiance pour les interfaces SaaS.
Contact Interactions plus humaines avec contexte, clarté et feedback immédiat.
Conversion Newsletter, recherche et checkout dans un langage visuel orienté action.
Auth Flow

Une scène login plus premium, avec structure claire et état positif visible.

Bienvenue
Continuez votre session en toute sécurité.
Checkout Experience

Un aperçu d'étape de paiement avec hiérarchie, sécurité et décision simplifiée.

Paiement sécurisé
Carte vérifiée, chiffrement actif, résumé prêt.

Une collection organisée par étapes du parcours.

On garde les memes templates et le meme code, mais la page prend le ton d'un showroom produit: authentification, relation utilisateur, puis conversion. Chaque groupe raconte une étape claire de l'expérience.

6 modèles / 3 intentions / sélection premium
Auth

Formulaires d'entrée et de création de compte.

Des flows pensés pour rassurer vite: validation claire, hiérarchie lisible et signaux de confiance pour les produits SaaS et apps web.

Login Register
1. Login Form

Formulaire de connexion avec validation email/password en temps réel.

Débutant JS
Connexion 👋
Ravi de vous revoir !
Email invalide.
6 caractères minimum.
ou

Pas de compte ? S'inscrire

✅ Connexion réussie ! Bienvenue.
HTML
<form class="login-form" id="login-form" novalidate>
  <h2>Connexion 👋</h2>
  <p>Ravi de vous revoir !</p>

  <div class="form-group">
    <label for="email">Email <span>*</span></label>
    <input type="email" id="email" name="email"
      placeholder="vous@exemple.fr" required />
    <span class="form-error" id="email-error">Email invalide.</span>
  </div>

  <div class="form-group">
    <label for="password">Mot de passe <span>*</span></label>
    <input type="password" id="password" name="password"
      placeholder="••••••••" required minlength="6" />
    <span class="form-error" id="password-error">6 caractères minimum.</span>
  </div>

  <a href="#" class="form-forgot">Mot de passe oublié ?</a>

  <button type="submit" class="form-submit">Se connecter →</button>

  <p class="form-footer">
    Pas de compte ? <a href="register.html">S'inscrire</a>
  </p>

  <div class="form-success" id="form-success" hidden>
    ✅ Connexion réussie !
  </div>
</form>
CSS
.login-form { background:#fff; border-radius:20px; border:1px solid #e5e7eb; padding:40px; max-width:420px; width:100%; }
.login-form h2 { font-size:1.5rem; font-weight:800; color:#1A1A2E; margin-bottom:6px; }
.login-form > p { color:#6b7280; font-size:.875rem; margin-bottom:28px; }
.form-group { margin-bottom:20px; }
.form-group label { display:block; font-size:.875rem; font-weight:600; color:#1A1A2E; margin-bottom:8px; }
.form-group label span { color:#FF6584; }
.form-group input {
  width:100%; padding:12px 16px;
  background:#f9fafb; border:1.5px solid #e5e7eb; border-radius:12px;
  color:#1A1A2E; font-size:.875rem; outline:none; transition:all .2s;
}
.form-group input:focus { border-color:#6C63FF; box-shadow:0 0 0 4px rgba(108,99,255,.12); background:#fff; }
.form-group input.error   { border-color:#EF4444; }
.form-group input.success { border-color:#10B981; }
.form-error { font-size:.75rem; color:#EF4444; margin-top:4px; display:none; }
.form-error.show { display:block; }
.form-forgot { display:block; text-align:right; font-size:.8rem; color:#6C63FF; text-decoration:none; margin-bottom:20px; font-weight:600; }
.form-submit {
  width:100%; padding:13px; background:linear-gradient(135deg,#6C63FF,#0FCCCE);
  color:white; border:none; border-radius:999px; font-size:.875rem; font-weight:700;
  cursor:pointer; transition:all .2s;
}
.form-submit:hover { transform:translateY(-2px); box-shadow:0 8px 20px rgba(108,99,255,.35); }
.form-footer { text-align:center; font-size:.875rem; color:#6b7280; margin-top:20px; }
.form-footer a { color:#6C63FF; font-weight:600; text-decoration:none; }
.form-success { background:rgba(16,185,129,.1); border:1px solid rgba(16,185,129,.3); border-radius:12px; padding:14px; text-align:center; color:#10B981; font-weight:600; font-size:.875rem; margin-top:16px; }
JavaScript
const loginForm = document.getElementById('login-form');

// Validation en temps réel
loginForm.querySelector('#email').addEventListener('input', validateEmail);
loginForm.querySelector('#password').addEventListener('input', validatePassword);

function validateEmail() {
  const input = document.getElementById('email');
  const error = document.getElementById('email-error');
  const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value.trim());
  setFieldState(input, error, isValid || !input.value);
  return isValid;
}

function validatePassword() {
  const input = document.getElementById('password');
  const error = document.getElementById('password-error');
  const isValid = input.value.length >= 6;
  setFieldState(input, error, isValid || !input.value);
  return isValid;
}

function setFieldState(input, error, isValid) {
  input.classList.toggle('error',   !isValid);
  input.classList.toggle('success',  isValid && input.value !== '');
  error.classList.toggle('show',    !isValid && input.value !== '');
}

loginForm.addEventListener('submit', function(e) {
  e.preventDefault();
  const emailOk = validateEmail();
  const passOk  = validatePassword();
  if (emailOk && passOk) {
    document.getElementById('form-success').removeAttribute('hidden');
  }
});
2. Register Form

Inscription avec force de mot de passe et confirmation.

IntermédiaireJS
Créer un compte ✨
Commencez dès aujourd'hui — gratuit !
Email invalide.
Entrez au moins 8 caractères
Les mots de passe ne correspondent pas.
🎉 Compte créé ! Vérifiez votre email.
HTML
<form class="register-form" id="register-form" novalidate>
  <h2>Créer un compte ✨</h2>
  <p>Commencez dès aujourd'hui — gratuit !</p>

  <div class="form-row">
    <div class="form-group">
      <label for="first-name">Prénom</label>
      <input type="text" id="first-name" placeholder="Jean" />
    </div>
    <div class="form-group">
      <label for="last-name">Nom</label>
      <input type="text" id="last-name" placeholder="Dupont" />
    </div>
  </div>

  <div class="form-group">
    <label for="email">Email <span>*</span></label>
    <input type="email" id="email" placeholder="vous@exemple.fr" required />
    <span class="form-error" id="email-error">Email invalide.</span>
  </div>

  <div class="form-group">
    <label for="password">Mot de passe <span>*</span></label>
    <input type="password" id="password" placeholder="••••••••" required />
    <div class="password-strength">
      <div class="password-strength__bar" id="strength-bar"></div>
    </div>
    <span class="form-hint" id="strength-hint">Entrez 8 caractères minimum</span>
  </div>

  <div class="form-group">
    <label for="confirm">Confirmation <span>*</span></label>
    <input type="password" id="confirm" placeholder="••••••••" required />
    <span class="form-error" id="confirm-error">Les mots de passe ne correspondent pas.</span>
  </div>

  <div class="form-checkbox">
    <input type="checkbox" id="cgu" required />
    <label for="cgu">J'accepte les <a href="#">CGU</a></label>
  </div>

  <button type="submit">Créer mon compte →</button>
</form>
JavaScript
// Indicateur de force du mot de passe
function checkPasswordStrength(password) {
  let score = 0;
  if (password.length >= 8)             score++;
  if (/[A-Z]/.test(password))          score++;
  if (/[0-9]/.test(password))          score++;
  if (/[^A-Za-z0-9]/.test(password))   score++;
  return score;
}

document.getElementById('password').addEventListener('input', function() {
  const score = checkPasswordStrength(this.value);
  const bar   = document.getElementById('strength-bar');
  const hint  = document.getElementById('strength-hint');
  const colors = ['#EF4444','#F59E0B','#3B82F6','#10B981'];
  const labels = ['Très faible','Faible','Moyen','Fort'];
  bar.style.width  = (score * 25) + '%';
  bar.style.background = colors[score - 1] || '#e5e7eb';
  hint.textContent = score > 0 ? labels[score - 1] : 'Entrez 8 caractères minimum';
});

// Validation formulaire
document.getElementById('register-form').addEventListener('submit', function(e) {
  e.preventDefault();
  const email   = document.getElementById('email');
  const pass    = document.getElementById('password');
  const confirm = document.getElementById('confirm');
  const cgu     = document.getElementById('cgu');
  let valid = true;

  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
    showError('email-error'); valid = false;
  } else hideError('email-error');

  if (pass.value !== confirm.value) {
    showError('confirm-error'); valid = false;
  } else hideError('confirm-error');

  if (valid && cgu.checked) {
    document.getElementById('success-msg').removeAttribute('hidden');
  }
});

function showError(id) { document.getElementById(id).classList.add('show'); }
function hideError(id)  { document.getElementById(id).classList.remove('show'); }
Contact

Relation, recherche et engagement.

Ici, le formulaire devient un point de contact. On privilégie les états fluides, la clarté du message et une sensation d'interface éditoriale plutôt que purement utilitaire.

Contact Newsletter Search
3. Contact Form

Formulaire de contact complet avec sujet, message et validation.

IntermédiaireJS
Contactez-nous 📬
Nous répondons sous 24h.
Message requis (10 caractères min.).
✅ Message envoyé ! Merci, nous reviendrons vers vous.
HTML
<form class="contact-form" id="contact-form" novalidate>
  <h2>Contactez-nous 📬</h2>
  <p>Nous répondons sous 24h.</p>

  <div class="form-row">
    <div class="form-group">
      <label for="name">Nom <span>*</span></label>
      <input type="text" id="name" placeholder="Jean Dupont" required />
      <span class="form-error" id="name-error">Nom requis.</span>
    </div>
    <div class="form-group">
      <label for="email">Email <span>*</span></label>
      <input type="email" id="email" placeholder="vous@exemple.fr" required />
      <span class="form-error" id="email-error">Email invalide.</span>
    </div>
  </div>

  <div class="form-group">
    <label for="subject">Sujet</label>
    <select id="subject">
      <option value="">Choisir un sujet…</option>
      <option>Question technique</option>
      <option>Partenariat</option>
      <option>Autre</option>
    </select>
  </div>

  <div class="form-group">
    <label for="message">Message <span>*</span></label>
    <textarea id="message" placeholder="Décrivez votre demande…" rows="5" required></textarea>
    <span class="form-error" id="message-error">Message requis (10 car. min.).</span>
  </div>

  <button type="submit">Envoyer le message →</button>
  <div class="form-success" id="form-success" hidden>
    ✅ Message envoyé ! Merci, on vous répond bientôt.
  </div>
</form>
JavaScript
document.getElementById('contact-form').addEventListener('submit', function(e) {
  e.preventDefault();
  let valid = true;

  const name    = document.getElementById('name');
  const email   = document.getElementById('email');
  const message = document.getElementById('message');

  // Nom
  if (!name.value.trim()) {
    showErr(name, 'name-error'); valid = false;
  } else clearErr(name, 'name-error');

  // Email
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
    showErr(email, 'email-error'); valid = false;
  } else clearErr(email, 'email-error');

  // Message
  if (message.value.trim().length < 10) {
    showErr(message, 'message-error'); valid = false;
  } else clearErr(message, 'message-error');

  if (valid) {
    document.getElementById('form-success').removeAttribute('hidden');
    this.reset();
  }
});

function showErr(input, errId) {
  input.classList.add('error');
  input.classList.remove('success');
  document.getElementById(errId).classList.add('show');
}
function clearErr(input, errId) {
  input.classList.remove('error');
  input.classList.add('success');
  document.getElementById(errId).classList.remove('show');
}
4. Newsletter Form

Formulaire d'abonnement compact avec animation de succès.

DébutantJS
📧
Restez informé
Rejoignez 12 000+ abonnés. Pas de spam, promis !
Veuillez entrer un email valide.
🎉 Bienvenue dans la communauté !

Désabonnement en 1 clic à tout moment.

HTML
<div class="newsletter-form">
  <span class="newsletter-form__icon">📧</span>
  <h3>Restez informé</h3>
  <p>Rejoignez 12 000+ abonnés. Pas de spam !</p>

  <form id="nl-form" novalidate>
    <div class="form-group">
      <input type="email" id="nl-email"
        placeholder="votre@email.fr" required />
      <span class="form-error" id="nl-error">Email invalide.</span>
    </div>
    <button type="submit">S'abonner gratuitement →</button>
  </form>

  <div class="form-success" id="nl-success" hidden>
    🎉 Bienvenue dans la communauté !
  </div>
  <p class="newsletter-form__note">
    Désabonnement en 1 clic à tout moment.
  </p>
</div>
JavaScript
document.getElementById('nl-form').addEventListener('submit', function(e) {
  e.preventDefault();
  const email = document.getElementById('nl-email');
  const error = document.getElementById('nl-error');
  const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim());

  if (!isValid) {
    email.classList.add('error');
    error.classList.add('show');
    return;
  }

  email.classList.remove('error');
  error.classList.remove('show');
  document.getElementById('nl-success').removeAttribute('hidden');
  this.style.display = 'none';
});
5. Search Form

Barre de recherche avec suggestions dynamiques et filtres.

DébutantJS
Rechercher 🔍
Trouvez exactement ce dont vous avez besoin.
🔍
HTML
<div class="search-form">
  <h3>Rechercher 🔍</h3>

  <div class="search-wrap">
    <span class="search-icon">🔍</span>
    <input type="search" id="search-input"
      placeholder="Rechercher des templates…" />
    <button class="search-clear" id="clear-btn">×</button>
  </div>

  <!-- Filtres -->
  <div class="search-filters">
    <button class="filter-chip active" data-filter="all">Tous</button>
    <button class="filter-chip" data-filter="html">HTML</button>
    <button class="filter-chip" data-filter="css">CSS</button>
    <button class="filter-chip" data-filter="js">JS</button>
  </div>

  <!-- Résultats -->
  <div id="results-container"></div>
</div>
JavaScript
const ITEMS = [
  { label:'Navbar Simple', tags:['html','css'] },
  { label:'Navbar Dashboard', tags:['html','css','js'] },
  { label:'Footer Newsletter', tags:['html','css','js'] },
  { label:'Product Card', tags:['html','css'] },
  { label:'Login Form', tags:['html','css','js'] },
];

let activeFilter = 'all';

function searchItems(query) {
  const q = query.trim().toLowerCase();
  return ITEMS.filter(item => {
    const matchQ = !q || item.label.toLowerCase().includes(q);
    const matchF = activeFilter === 'all' || item.tags.includes(activeFilter);
    return matchQ && matchF;
  });
}

document.getElementById('search-input').addEventListener('input', function() {
  renderResults(searchItems(this.value));
});

document.querySelectorAll('.filter-chip').forEach(chip => {
  chip.addEventListener('click', function() {
    document.querySelectorAll('.filter-chip').forEach(c => c.classList.remove('active'));
    this.classList.add('active');
    activeFilter = this.dataset.filter;
    renderResults(searchItems(document.getElementById('search-input').value));
  });
});

document.getElementById('clear-btn').addEventListener('click', function() {
  document.getElementById('search-input').value = '';
  renderResults([]);
});

function renderResults(items) {
  const container = document.getElementById('results-container');
  if (!items.length) { container.innerHTML = ''; return; }
  container.innerHTML = items.map(i =>
    `<div class="search-result">${i.label}</div>`
  ).join('');
}
Conversion

Checkout et décision finale.

Une dernière étape plus orientée conversion, avec progression, réduction du doute et sensation de tunnel premium jusqu'à la confirmation.

Checkout Multi-step
6. Checkout Form

Formulaire de paiement multi-étapes avec validation complète.

AvancéJS
1
2
3
Livraison 📦
Nom requis.
Adresse requise.
HTML
<div class="checkout-form">
  <!-- Indicateur d'étapes -->
  <div class="steps-indicator">
    <div class="step active" id="step-1">1</div>
    <div class="step-line"><div id="progress-line"></div></div>
    <div class="step" id="step-2">2</div>
    <div class="step-line"></div>
    <div class="step" id="step-3">3</div>
  </div>

  <!-- Étape 1 — Livraison -->
  <div id="checkout-step-1" class="checkout-step">
    <h3>Livraison 📦</h3>
    <div class="form-group">
      <label for="full-name">Nom complet</label>
      <input type="text" id="full-name" placeholder="Jean Dupont" />
    </div>
    <div class="form-group">
      <label for="address">Adresse</label>
      <input type="text" id="address" placeholder="12 rue des Fleurs, Paris" />
    </div>
    <button class="form-submit" id="next-1">Continuer →</button>
  </div>

  <!-- Étape 2 — Paiement -->
  <div id="checkout-step-2" class="checkout-step" hidden>
    <h3>Paiement 💳</h3>
    <div class="form-group">
      <label for="card-num">Numéro de carte</label>
      <input type="text" id="card-num" placeholder="•••• •••• •••• ••••" maxlength="19" />
    </div>
    <div class="form-row">
      <div class="form-group">
        <label for="expiry">Expiration</label>
        <input type="text" id="expiry" placeholder="MM/AA" maxlength="5" />
      </div>
      <div class="form-group">
        <label for="cvv">CVV</label>
        <input type="text" id="cvv" placeholder="•••" maxlength="3" />
      </div>
    </div>
    <div class="form-row-btns">
      <button id="prev-2">← Retour</button>
      <button class="form-submit" id="next-2">Confirmer →</button>
    </div>
  </div>

  <!-- Étape 3 — Confirmation -->
  <div id="checkout-step-3" class="checkout-step checkout-success" hidden>
    <span>🎉</span>
    <h3>Commande confirmée !</h3>
    <p>Email de confirmation envoyé.</p>
    <div class="form-success">#2025-FR-8492</div>
  </div>
</div>
JavaScript
let currentStep = 1;

function goToStep(step) {
  // Masquer toutes les étapes
  document.querySelectorAll('.checkout-step').forEach(s => s.setAttribute('hidden', ''));
  // Afficher l'étape demandée
  document.getElementById(`checkout-step-${step}`).removeAttribute('hidden');
  // Mettre à jour les indicateurs
  document.querySelectorAll('.step').forEach((el, i) => {
    el.classList.toggle('active',   i < step);
    el.classList.toggle('complete', i < step - 1);
  });
  currentStep = step;
}

// Formater le numéro de carte (XXXX XXXX XXXX XXXX)
document.getElementById('card-num').addEventListener('input', function() {
  let val = this.value.replace(/\D/g, '').slice(0, 16);
  this.value = val.match(/.{1,4}/g)?.join(' ') ?? val;
});

// Formater la date d'expiration (MM/AA)
document.getElementById('expiry').addEventListener('input', function() {
  let val = this.value.replace(/\D/g, '').slice(0, 4);
  if (val.length >= 2) val = val.slice(0,2) + '/' + val.slice(2);
  this.value = val;
});

// Navigation
document.getElementById('next-1').addEventListener('click', () => {
  const name = document.getElementById('full-name').value.trim();
  const addr = document.getElementById('address').value.trim();
  if (name && addr) goToStep(2);
});

document.getElementById('prev-2').addEventListener('click', () => goToStep(1));
document.getElementById('next-2').addEventListener('click', () => {
  const card = document.getElementById('card-num').value.replace(/\s/g,'');
  if (card.length === 16) goToStep(3);
});

// Initialiser
goToStep(1);