Bootstrap
Bootstrap5
Form
Step
Html
Js
Template de formulaire étape par étape Bootstrap 5 avec validation et navigation entre les étapes.
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="copyright" content="MEZGANI Said" />
<meta name="author" content="AngularForAll" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Formulaire Multi-étapes Bootstrap 5 | AngularForAll</title>
<!-- Bootstrap 5 + Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.form-container {
max-width: 800px;
width: 100%;
margin: 0 auto;
}
.form-card {
background: white;
border-radius: 32px;
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15);
padding: 2.5rem;
overflow: hidden;
}
/* Stepper */
.stepper {
display: flex;
justify-content: space-between;
margin-bottom: 3rem;
position: relative;
}
.stepper::before {
content: '';
position: absolute;
top: 25px;
left: 40px;
right: 40px;
height: 3px;
background: #e9ecef;
z-index: 1;
}
.stepper-progress {
position: absolute;
top: 25px;
left: 40px;
height: 3px;
background: linear-gradient(90deg, #667eea, #764ba2);
z-index: 2;
transition: width 0.3s ease;
width: 0%;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 3;
}
.step-circle {
width: 50px;
height: 50px;
border-radius: 50%;
background: white;
border: 3px solid #e9ecef;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.2rem;
color: #6c757d;
margin-bottom: 0.5rem;
transition: all 0.3s ease;
}
.step-item.active .step-circle {
border-color: #667eea;
background: #667eea;
color: white;
box-shadow: 0 0 0 5px rgba(102, 126, 234, 0.2);
}
.step-item.completed .step-circle {
border-color: #667eea;
background: #667eea;
color: white;
}
.step-item.completed .step-circle i {
display: block;
}
.step-item.completed .step-circle span {
display: none;
}
.step-item .step-circle i {
display: none;
font-size: 1.3rem;
}
.step-label {
font-weight: 600;
color: #6c757d;
font-size: 0.9rem;
}
.step-item.active .step-label {
color: #667eea;
font-weight: 700;
}
.step-item.completed .step-label {
color: #667eea;
}
/* Form Steps */
.form-step {
display: none;
animation: fadeIn 0.4s ease;
}
.form-step.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.step-title {
font-weight: 700;
font-size: 1.8rem;
color: #2d3748;
margin-bottom: 0.5rem;
}
.step-description {
color: #718096;
margin-bottom: 2rem;
}
/* Form Controls */
.form-label {
font-weight: 600;
color: #4a5568;
margin-bottom: 0.5rem;
}
.form-control, .form-select {
border: 2px solid #e2e8f0;
border-radius: 14px;
padding: 0.9rem 1.2rem;
font-size: 1rem;
transition: all 0.2s;
background-color: #fafbfc;
}
.form-control:focus, .form-select:focus {
border-color: #667eea;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.15);
background-color: white;
}
.input-group-text {
background: #fafbfc;
border: 2px solid #e2e8f0;
border-right: none;
border-radius: 14px 0 0 14px;
color: #718096;
}
.input-group .form-control {
border-left: none;
border-radius: 0 14px 14px 0;
}
/* Radio & Checkbox Cards */
.option-card {
border: 2px solid #e2e8f0;
border-radius: 16px;
padding: 1.2rem;
cursor: pointer;
transition: all 0.2s;
height: 100%;
background: #fafbfc;
}
.option-card:hover {
border-color: #a0aec0;
background: white;
}
.option-card.selected {
border-color: #667eea;
background: #f3f4ff;
}
.option-card input {
display: none;
}
.option-card i {
font-size: 2rem;
color: #667eea;
margin-bottom: 0.8rem;
}
.option-card h6 {
font-weight: 700;
margin-bottom: 0.3rem;
color: #2d3748;
}
.option-card p {
font-size: 0.85rem;
color: #718096;
margin: 0;
}
/* Payment Options */
.payment-option {
border: 2px solid #e2e8f0;
border-radius: 16px;
padding: 1rem 1.5rem;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 0.8rem;
background: #fafbfc;
}
.payment-option:hover {
border-color: #a0aec0;
background: white;
}
.payment-option.selected {
border-color: #667eea;
background: #f3f4ff;
}
.payment-option input {
display: none;
}
.payment-option i {
font-size: 1.8rem;
color: #667eea;
}
/* Summary */
.summary-box {
background: #f7fafc;
border-radius: 20px;
padding: 1.5rem;
border: 2px solid #e2e8f0;
}
.summary-item {
display: flex;
justify-content: space-between;
padding: 0.8rem 0;
border-bottom: 1px solid #e2e8f0;
}
.summary-item:last-child {
border-bottom: none;
}
.summary-label {
color: #718096;
font-weight: 500;
}
.summary-value {
font-weight: 600;
color: #2d3748;
}
.total-price {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-radius: 16px;
padding: 1.2rem;
margin-top: 1rem;
}
/* Buttons */
.btn-prev {
background: white;
border: 2px solid #e2e8f0;
color: #4a5568;
border-radius: 14px;
padding: 0.9rem 2rem;
font-weight: 600;
transition: all 0.2s;
}
.btn-prev:hover {
background: #f7fafc;
border-color: #cbd5e0;
}
.btn-next, .btn-submit {
background: linear-gradient(135deg, #667eea, #764ba2);
border: none;
color: white;
border-radius: 14px;
padding: 0.9rem 2rem;
font-weight: 600;
transition: all 0.2s;
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
}
.btn-next:hover, .btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.btn-submit {
background: linear-gradient(135deg, #48bb78, #38a169);
box-shadow: 0 6px 20px rgba(72, 187, 120, 0.3);
}
/* Success Message */
.success-message {
text-align: center;
padding: 2rem 0;
}
.success-icon {
width: 90px;
height: 90px;
background: linear-gradient(135deg, #48bb78, #38a169);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 1.5rem;
}
.success-icon i {
font-size: 3.5rem;
color: white;
}
/* Responsive */
@media (max-width: 576px) {
.form-card {
padding: 1.5rem;
}
.stepper::before {
left: 20px;
right: 20px;
}
.stepper-progress {
left: 20px;
}
.step-label {
font-size: 0.75rem;
}
.step-title {
font-size: 1.5rem;
}
.btn-prev, .btn-next, .btn-submit {
padding: 0.7rem 1.2rem;
}
}
</style>
</head>
<body>
<div class="form-container">
<div class="form-card">
<!-- Stepper -->
<div class="stepper">
<div class="stepper-progress" id="stepperProgress" style="width: 0%;"></div>
<div class="step-item active" data-step="1">
<div class="step-circle">
<span>1</span>
<i class="bi bi-check-lg"></i>
</div>
<div class="step-label">Compte</div>
</div>
<div class="step-item" data-step="2">
<div class="step-circle">
<span>2</span>
<i class="bi bi-check-lg"></i>
</div>
<div class="step-label">Profil</div>
</div>
<div class="step-item" data-step="3">
<div class="step-circle">
<span>3</span>
<i class="bi bi-check-lg"></i>
</div>
<div class="step-label">Formule</div>
</div>
<div class="step-item" data-step="4">
<div class="step-circle">
<span>4</span>
<i class="bi bi-check-lg"></i>
</div>
<div class="step-label">Paiement</div>
</div>
</div>
<!-- Formulaire -->
<form id="multiStepForm">
<!-- Step 1: Compte -->
<div class="form-step active" data-step="1">
<h2 class="step-title">Créez votre compte</h2>
<p class="step-description">Commencez par renseigner vos informations de connexion.</p>
<div class="mb-4">
<label class="form-label">Nom complet</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-person"></i></span>
<input type="text" class="form-control" id="fullName" placeholder="Jean Dupont" required>
</div>
</div>
<div class="mb-4">
<label class="form-label">Email professionnel</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
<input type="email" class="form-control" id="email" placeholder="jean@entreprise.fr" required>
</div>
</div>
<div class="mb-4">
<label class="form-label">Mot de passe</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control" id="password" placeholder="••••••••" required>
</div>
<small class="text-muted">Minimum 8 caractères, avec chiffres et lettres</small>
</div>
<div class="mb-4">
<label class="form-label">Confirmer le mot de passe</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-lock-fill"></i></span>
<input type="password" class="form-control" id="confirmPassword" placeholder="••••••••" required>
</div>
</div>
</div>
<!-- Step 2: Profil -->
<div class="form-step" data-step="2">
<h2 class="step-title">Complétez votre profil</h2>
<p class="step-description">Ces informations nous aident à personnaliser votre expérience.</p>
<div class="row mb-4">
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label">Téléphone</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-telephone"></i></span>
<input type="tel" class="form-control" id="phone" placeholder="06 12 34 56 78">
</div>
</div>
<div class="col-md-6">
<label class="form-label">Date de naissance</label>
<input type="date" class="form-control" id="birthdate">
</div>
</div>
<div class="mb-4">
<label class="form-label">Pays / Région</label>
<select class="form-select" id="country">
<option selected disabled>Choisissez votre pays</option>
<option>France</option>
<option>Belgique</option>
<option>Suisse</option>
<option>Canada</option>
<option>Luxembourg</option>
<option>Autre</option>
</select>
</div>
<div class="mb-4">
<label class="form-label">Adresse</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-geo-alt"></i></span>
<input type="text" class="form-control" id="address" placeholder="Rue, numéro">
</div>
</div>
<div class="row">
<div class="col-md-8 mb-3 mb-md-0">
<label class="form-label">Ville</label>
<input type="text" class="form-control" id="city" placeholder="Paris">
</div>
<div class="col-md-4">
<label class="form-label">Code postal</label>
<input type="text" class="form-control" id="zipCode" placeholder="75000">
</div>
</div>
</div>
<!-- Step 3: Formule -->
<div class="form-step" data-step="3">
<h2 class="step-title">Choisissez votre formule</h2>
<p class="step-description">Sélectionnez l'offre qui correspond le mieux à vos besoins.</p>
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="option-card" data-plan="basic">
<input type="radio" name="plan" value="basic" id="planBasic">
<i class="bi bi-star"></i>
<h6>Basic</h6>
<p class="mb-1"><strong>19€</strong> / mois</p>
<p>Fonctionnalités essentielles</p>
</div>
</div>
<div class="col-md-4">
<div class="option-card selected" data-plan="pro">
<input type="radio" name="plan" value="pro" id="planPro" checked>
<i class="bi bi-gem"></i>
<h6>Pro</h6>
<p class="mb-1"><strong>39€</strong> / mois</p>
<p>Pour les professionnels</p>
<span class="badge bg-success mt-1">Populaire</span>
</div>
</div>
<div class="col-md-4">
<div class="option-card" data-plan="enterprise">
<input type="radio" name="plan" value="enterprise" id="planEnterprise">
<i class="bi bi-building"></i>
<h6>Enterprise</h6>
<p class="mb-1"><strong>79€</strong> / mois</p>
<p>Solutions avancées</p>
</div>
</div>
</div>
<div class="mb-4">
<label class="form-label">Durée d'engagement</label>
<select class="form-select" id="duration">
<option selected>Mensuel (sans engagement)</option>
<option>Annuel (-20% de réduction)</option>
</select>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="addons1">
<label class="form-check-label" for="addons1">
Support prioritaire (+9€/mois)
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="addons2">
<label class="form-check-label" for="addons2">
Stockage supplémentaire 100Go (+15€/mois)
</label>
</div>
</div>
<!-- Step 4: Paiement -->
<div class="form-step" data-step="4">
<h2 class="step-title">Paiement</h2>
<p class="step-description">Finalisez votre inscription avec vos informations de paiement.</p>
<div class="mb-4">
<label class="form-label">Méthode de paiement</label>
<div class="payment-option selected" data-payment="card">
<input type="radio" name="payment" value="card" id="payCard" checked>
<div class="d-flex align-items-center">
<i class="bi bi-credit-card me-3"></i>
<div>
<h6 class="mb-0 fw-bold">Carte bancaire</h6>
<small class="text-muted">Visa, Mastercard, American Express</small>
</div>
</div>
</div>
<div class="payment-option" data-payment="paypal">
<input type="radio" name="payment" value="paypal" id="payPaypal">
<div class="d-flex align-items-center">
<i class="bi bi-paypal me-3"></i>
<div>
<h6 class="mb-0 fw-bold">PayPal</h6>
<small class="text-muted">Paiement sécurisé via PayPal</small>
</div>
</div>
</div>
</div>
<div id="cardDetails">
<div class="mb-4">
<label class="form-label">Numéro de carte</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-credit-card"></i></span>
<input type="text" class="form-control" id="cardNumber" placeholder="1234 5678 9012 3456">
</div>
</div>
<div class="row mb-4">
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label">Date d'expiration</label>
<input type="text" class="form-control" id="expiry" placeholder="MM/AA">
</div>
<div class="col-md-6">
<label class="form-label">CVV</label>
<input type="text" class="form-control" id="cvv" placeholder="123">
</div>
</div>
<div class="mb-4">
<label class="form-label">Nom sur la carte</label>
<input type="text" class="form-control" id="cardName" placeholder="Jean Dupont">
</div>
</div>
<!-- Résumé de commande -->
<div class="summary-box">
<h6 class="fw-bold mb-3"><i class="bi bi-cart me-2"></i>Récapitulatif</h6>
<div class="summary-item">
<span class="summary-label">Formule</span>
<span class="summary-value" id="summaryPlan">Pro</span>
</div>
<div class="summary-item">
<span class="summary-label">Engagement</span>
<span class="summary-value" id="summaryDuration">Mensuel</span>
</div>
<div class="summary-item">
<span class="summary-label">Options</span>
<span class="summary-value" id="summaryAddons">Aucune</span>
</div>
<div class="total-price">
<div class="d-flex justify-content-between align-items-center">
<span class="fw-bold">Total</span>
<span class="fw-bold fs-4" id="totalPrice">39€ / mois</span>
</div>
</div>
</div>
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="terms" required>
<label class="form-check-label small" for="terms">
J'accepte les <a href="#">conditions générales</a> et la <a href="#">politique de confidentialité</a>
</label>
</div>
</div>
<!-- Success Step -->
<div class="form-step" data-step="5">
<div class="success-message">
<div class="success-icon">
<i class="bi bi-check-lg"></i>
</div>
<h2 class="step-title mb-3">Inscription réussie !</h2>
<p class="step-description mb-4">Merci d'avoir choisi notre service. Vous allez recevoir un email de confirmation dans quelques instants.</p>
<p class="text-muted mb-4">Vous allez être redirigé vers votre tableau de bord...</p>
<button type="button" class="btn btn-success btn-lg rounded-pill px-5" onclick="location.reload()">
<i class="bi bi-arrow-right-circle me-2"></i>Accéder au dashboard
</button>
</div>
</div>
<!-- Navigation Buttons -->
<div class="d-flex justify-content-between mt-4">
<button type="button" class="btn btn-prev" id="prevBtn" disabled>
<i class="bi bi-arrow-left me-2"></i>Précédent
</button>
<button type="button" class="btn btn-next" id="nextBtn">
Suivant <i class="bi bi-arrow-right ms-2"></i>
</button>
<button type="submit" class="btn btn-submit" id="submitBtn" style="display: none;">
<i class="bi bi-check-lg me-2"></i>Confirmer l'inscription
</button>
</div>
</form>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
(function() {
'use strict';
// Éléments
const form = document.getElementById('multiStepForm');
const steps = document.querySelectorAll('.form-step');
const stepItems = document.querySelectorAll('.step-item');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const submitBtn = document.getElementById('submitBtn');
const stepperProgress = document.getElementById('stepperProgress');
let currentStep = 1;
const totalSteps = 4; // 4 étapes de formulaire, la 5ème est le succès
// Mise à jour du stepper
function updateStepper(step) {
// Mettre à jour les classes des step-items
stepItems.forEach((item, index) => {
const stepNum = index + 1;
item.classList.remove('active', 'completed');
if (stepNum === step) {
item.classList.add('active');
} else if (stepNum < step) {
item.classList.add('completed');
}
});
// Mettre à jour la barre de progression
const progressWidth = ((step - 1) / (totalSteps - 1)) * 100;
stepperProgress.style.width = progressWidth + '%';
// Mettre à jour les boutons
if (step === 1) {
prevBtn.disabled = true;
} else {
prevBtn.disabled = false;
}
if (step === totalSteps) {
nextBtn.style.display = 'none';
submitBtn.style.display = 'block';
} else {
nextBtn.style.display = 'block';
submitBtn.style.display = 'none';
}
}
// Afficher l'étape
function showStep(step) {
steps.forEach((s, index) => {
s.classList.remove('active');
if (index + 1 === step) {
s.classList.add('active');
}
});
updateStepper(step);
// Mise à jour du résumé quand on arrive à l'étape 4
if (step === 4) {
updateSummary();
}
}
// Validation des étapes
function validateStep(step) {
if (step === 1) {
const fullName = document.getElementById('fullName').value;
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const confirm = document.getElementById('confirmPassword').value;
if (!fullName || !email || !password || !confirm) {
alert('Veuillez remplir tous les champs de l\'étape 1.');
return false;
}
if (password !== confirm) {
alert('Les mots de passe ne correspondent pas.');
return false;
}
if (password.length < 8) {
alert('Le mot de passe doit contenir au moins 8 caractères.');
return false;
}
return true;
}
if (step === 2) {
const phone = document.getElementById('phone').value;
const country = document.getElementById('country').value;
if (!phone || !country || country === 'Choisissez votre pays') {
alert('Veuillez remplir le téléphone et sélectionner un pays.');
return false;
}
return true;
}
if (step === 3) {
return true; // Toujours valide
}
if (step === 4) {
const terms = document.getElementById('terms');
if (!terms.checked) {
alert('Vous devez accepter les conditions générales.');
return false;
}
const payment = document.querySelector('input[name="payment"]:checked').value;
if (payment === 'card') {
const cardNumber = document.getElementById('cardNumber').value;
const expiry = document.getElementById('expiry').value;
const cvv = document.getElementById('cvv').value;
const cardName = document.getElementById('cardName').value;
if (!cardNumber || !expiry || !cvv || !cardName) {
alert('Veuillez remplir tous les champs de la carte bancaire.');
return false;
}
}
return true;
}
return true;
}
// Mise à jour du résumé
function updateSummary() {
// Plan
const planSelected = document.querySelector('input[name="plan"]:checked');
const planLabel = planSelected.closest('.option-card').querySelector('h6').innerText;
const planPrice = planSelected.closest('.option-card').querySelector('strong').innerText;
document.getElementById('summaryPlan').innerText = planLabel;
// Durée
const duration = document.getElementById('duration');
const durationText = duration.options[duration.selectedIndex].text;
document.getElementById('summaryDuration').innerText = durationText;
// Options
const addons1 = document.getElementById('addons1');
const addons2 = document.getElementById('addons2');
let addonsText = [];
let addonsPrice = 0;
if (addons1.checked) {
addonsText.push('Support prioritaire');
addonsPrice += 9;
}
if (addons2.checked) {
addonsText.push('Stockage 100Go');
addonsPrice += 15;
}
document.getElementById('summaryAddons').innerText = addonsText.length ? addonsText.join(', ') : 'Aucune';
// Total
let basePrice = parseInt(planPrice);
let total = basePrice + addonsPrice;
let priceText = total + '€ / mois';
if (duration.value === 'Annuel (-20% de réduction)') {
total = total * 0.8;
priceText = Math.round(total) + '€ / mois (facturé annuellement)';
}
document.getElementById('totalPrice').innerText = priceText;
}
// Gestion des cartes d'option
document.querySelectorAll('.option-card').forEach(card => {
card.addEventListener('click', function() {
document.querySelectorAll('.option-card').forEach(c => c.classList.remove('selected'));
this.classList.add('selected');
this.querySelector('input[type="radio"]').checked = true;
});
});
// Gestion des options de paiement
document.querySelectorAll('.payment-option').forEach(option => {
option.addEventListener('click', function() {
document.querySelectorAll('.payment-option').forEach(o => o.classList.remove('selected'));
this.classList.add('selected');
this.querySelector('input[type="radio"]').checked = true;
});
});
// Navigation
nextBtn.addEventListener('click', function(e) {
e.preventDefault();
if (validateStep(currentStep)) {
if (currentStep < totalSteps) {
currentStep++;
showStep(currentStep);
}
}
});
prevBtn.addEventListener('click', function(e) {
e.preventDefault();
if (currentStep > 1) {
currentStep--;
showStep(currentStep);
}
});
// Soumission du formulaire
form.addEventListener('submit', function(e) {
e.preventDefault();
if (validateStep(4)) {
// Afficher le message de succès
currentStep = 5;
steps.forEach(s => s.classList.remove('active'));
steps[4].classList.add('active');
// Cacher les boutons de navigation
prevBtn.style.display = 'none';
nextBtn.style.display = 'none';
submitBtn.style.display = 'none';
// Cacher le stepper
document.querySelector('.stepper').style.display = 'none';
}
});
// Mise à jour du résumé en temps réel
document.querySelectorAll('input[name="plan"], #duration, #addons1, #addons2').forEach(el => {
el.addEventListener('change', updateSummary);
});
})();
</script>
</body>
</html>