Bootstrap 5
Checkout
Stepper
E Commerce
Formulaire
Multi Etapes
Template
Html Css Js
Processus de commande multi-étapes Bootstrap 5 : stepper visuel, validation par étape, récapitulatif panier et confirmation finale responsive.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="copyright" content="AngularForAll" />
<meta name="author" content="AngularForAll" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="public, max-age=604800" />
<title>Snippets Checkout Step Boostrap5 2026 05020023 | AngularForAll</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
<style>
:root {
--step-primary: #4361ee;
--step-success: #06d6a0;
--step-pending: #e9ecef;
--step-text: #6c757d;
--step-active-text: #212529;
}
body {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
min-height: 100vh;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
/* ========== CONTENEUR PRINCIPAL ========== */
.checkout-container {
max-width: 900px;
margin: 0 auto;
padding: 2rem 1rem;
}
/* ========== CARTE PRINCIPALE ========== */
.checkout-card {
background: #ffffff;
border-radius: 24px;
border: none;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.checkout-header {
background: linear-gradient(135deg, #4361ee 0%, #3a0ca3 100%);
padding: 2rem 2rem 1.5rem;
color: white;
}
.checkout-body {
padding: 2rem;
}
/* ========== INDICATEUR D'ÉTAPES ========== */
.steps-indicator {
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 0 0.5rem;
margin-bottom: 2.5rem;
}
/* Ligne de connexion entre les étapes */
.steps-line {
position: absolute;
top: 28px;
left: calc(16.67% + 10px);
right: calc(16.67% + 10px);
height: 4px;
background: var(--step-pending);
border-radius: 2px;
z-index: 1;
transition: background 0.4s ease;
}
.steps-line-progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: linear-gradient(90deg, var(--step-primary), var(--step-success));
border-radius: 2px;
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 2;
}
/* Étape individuelle */
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 3;
flex: 1;
cursor: pointer;
transition: transform 0.2s ease;
}
.step-item:hover {
transform: translateY(-2px);
}
.step-circle {
width: 56px;
height: 56px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 0.75rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
background: var(--step-pending);
color: var(--step-text);
border: 3px solid transparent;
}
/* Étape active */
.step-item.active .step-circle {
background: white;
color: var(--step-primary);
border-color: var(--step-primary);
box-shadow: 0 0 0 8px rgba(67, 97, 238, 0.15), 0 8px 20px rgba(67, 97, 238, 0.2);
transform: scale(1.05);
}
/* Étape complétée */
.step-item.completed .step-circle {
background: var(--step-success);
color: white;
border-color: var(--step-success);
box-shadow: 0 0 0 8px rgba(6, 214, 160, 0.1), 0 4px 12px rgba(6, 214, 160, 0.2);
}
.step-item.completed .step-circle .step-icon::before {
content: '\F26E'; /* Bootstrap Icons check */
font-family: 'Bootstrap Icons';
font-weight: bold;
}
.step-label {
font-size: 0.85rem;
font-weight: 600;
color: var(--step-text);
text-transform: uppercase;
letter-spacing: 0.5px;
transition: color 0.3s ease;
text-align: center;
}
.step-item.active .step-label {
color: var(--step-primary);
font-weight: 700;
}
.step-item.completed .step-label {
color: var(--step-success);
}
.step-sublabel {
font-size: 0.7rem;
color: #adb5bd;
margin-top: 0.15rem;
text-align: center;
}
/* ========== CONTENU DES ÉTAPES ========== */
.step-content {
min-height: 300px;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 16px;
margin-top: 1rem;
}
.step-content h4 {
font-weight: 700;
color: #212529;
margin-bottom: 1rem;
}
.step-content p {
color: #6c757d;
margin-bottom: 1.5rem;
}
/* ========== BOUTONS DE NAVIGATION ========== */
.btn-prev {
background: white;
border: 2px solid #dee2e6;
color: #495057;
border-radius: 12px;
padding: 0.6rem 1.5rem;
font-weight: 600;
transition: all 0.2s ease;
}
.btn-prev:hover {
background: #f1f3f5;
border-color: #adb5bd;
}
.btn-next {
background: var(--step-primary);
border: 2px solid var(--step-primary);
color: white;
border-radius: 12px;
padding: 0.6rem 1.8rem;
font-weight: 600;
transition: all 0.2s ease;
box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
}
.btn-next:hover {
background: #3a56d4;
border-color: #3a56d4;
box-shadow: 0 6px 20px rgba(67, 97, 238, 0.4);
transform: translateY(-1px);
}
.btn-success-checkout {
background: var(--step-success);
border: 2px solid var(--step-success);
color: white;
border-radius: 12px;
padding: 0.6rem 2rem;
font-weight: 600;
box-shadow: 0 4px 15px rgba(6, 214, 160, 0.3);
transition: all 0.2s ease;
}
.btn-success-checkout:hover {
background: #05b88a;
border-color: #05b88a;
box-shadow: 0 6px 20px rgba(6, 214, 160, 0.4);
color: white;
}
/* ========== LISTE DE PRODUITS (ÉTAPE PANIER) ========== */
.cart-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: white;
border-radius: 12px;
margin-bottom: 0.75rem;
border: 1px solid #e9ecef;
transition: box-shadow 0.2s ease;
}
.cart-item:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.cart-item-img {
width: 60px;
height: 60px;
border-radius: 10px;
object-fit: cover;
background: #f1f3f5;
}
.cart-item-info {
flex: 1;
}
.cart-item-title {
font-weight: 600;
color: #212529;
margin-bottom: 0.15rem;
}
.cart-item-meta {
font-size: 0.8rem;
color: #adb5bd;
}
.cart-item-price {
font-weight: 700;
color: #212529;
}
/* ========== RESUME (ÉTAPE PAIEMENT) ========== */
.summary-line {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid #e9ecef;
font-size: 0.95rem;
}
.summary-total {
font-weight: 700;
font-size: 1.2rem;
border-bottom: none;
color: #212529;
}
/* ========== RESPONSIVE ========== */
@media (max-width: 768px) {
.checkout-header {
padding: 1.5rem 1.5rem 1.2rem;
}
.checkout-body {
padding: 1.5rem;
}
.step-circle {
width: 44px;
height: 44px;
font-size: 1.1rem;
}
.steps-line {
top: 22px;
}
.step-label {
font-size: 0.7rem;
}
.step-content {
padding: 1rem;
}
}
@media (max-width: 576px) {
.steps-indicator {
flex-direction: column;
gap: 1.5rem;
align-items: flex-start;
padding-left: 1rem;
}
.step-item {
flex-direction: row;
gap: 1rem;
flex: none;
width: 100%;
}
.step-circle {
margin-bottom: 0;
width: 40px;
height: 40px;
font-size: 1rem;
}
.steps-line {
display: none;
}
.step-item::after {
content: '';
position: absolute;
left: 19px;
top: 42px;
bottom: -24px;
width: 3px;
background: var(--step-pending);
z-index: 0;
}
.step-item:last-child::after {
display: none;
}
.step-item.completed::after {
background: var(--step-success);
}
.step-item.active::after {
background: linear-gradient(to bottom, var(--step-primary), var(--step-pending));
}
}
</style>
</head>
<body>
<div class="checkout-container">
<!-- Carte principale -->
<div class="checkout-card">
<!-- En-tête -->
<div class="checkout-header text-center">
<h2 class="fw-bold mb-1">
<i class="bi bi-cart-check me-2"></i>Finaliser la commande
</h2>
<p class="mb-0 opacity-75 small">Complétez les étapes ci-dessous</p>
</div>
<!-- Corps -->
<div class="checkout-body">
<!-- ========== INDICATEUR D'ÉTAPES ========== -->
<div class="steps-indicator" id="stepsIndicator">
<!-- Ligne de fond -->
<div class="steps-line">
<div class="steps-line-progress" id="stepsLineProgress" style="width: 0%;"></div>
</div>
<!-- Étape 1 : Panier -->
<div class="step-item active" data-step="1" onclick="goToStep(1)">
<div class="step-circle">
<span class="step-icon">1</span>
</div>
<div>
<div class="step-label">
<i class="bi bi-cart3 me-1"></i>Panier
</div>
<div class="step-sublabel">Vérifier les articles</div>
</div>
</div>
<!-- Étape 2 : Livraison -->
<div class="step-item" data-step="2" onclick="goToStep(2)">
<div class="step-circle">
<span class="step-icon">2</span>
</div>
<div>
<div class="step-label">
<i class="bi bi-truck me-1"></i>Livraison
</div>
<div class="step-sublabel">Adresse et mode</div>
</div>
</div>
<!-- Étape 3 : Paiement -->
<div class="step-item" data-step="3" onclick="goToStep(3)">
<div class="step-circle">
<span class="step-icon">3</span>
</div>
<div>
<div class="step-label">
<i class="bi bi-credit-card me-1"></i>Paiement
</div>
<div class="step-sublabel">Finaliser l'achat</div>
</div>
</div>
</div>
<!-- ========== CONTENU DYNAMIQUE ========== -->
<div class="step-content" id="stepContent">
<!-- Rempli dynamiquement par JavaScript -->
</div>
<!-- ========== BOUTONS DE NAVIGATION ========== -->
<div class="d-flex justify-content-between align-items-center mt-4" id="navigationButtons">
<button class="btn btn-prev" id="btnPrev" onclick="prevStep()" disabled>
<i class="bi bi-arrow-left me-1"></i>Précédent
</button>
<span class="text-muted small" id="stepCounter">Étape 1/3</span>
<button class="btn btn-next" id="btnNext" onclick="nextStep()">
Suivant<i class="bi bi-arrow-right ms-1"></i>
</button>
</div>
</div>
</div>
<!-- Indicateur de confiance -->
<div class="text-center mt-3">
<small class="text-muted">
<i class="bi bi-shield-check text-success me-1"></i>
Paiement 100% sécurisé • SSL crypté
</small>
</div>
</div>
<!-- Bootstrap 5 JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Script du CheckoutSteps -->
<script>
(function() {
// ============ CONFIGURATION ============
const TOTAL_STEPS = 3;
let currentStep = 1; // 1 = Panier, 2 = Livraison, 3 = Paiement
// Historique de complétion (empêche d'aller à l'étape 3 sans avoir validé la 2, etc.)
const completedSteps = new Set();
// ============ ÉLÉMENTS DOM ============
const stepItems = document.querySelectorAll('.step-item');
const stepsLineProgress = document.getElementById('stepsLineProgress');
const stepContent = document.getElementById('stepContent');
const btnPrev = document.getElementById('btnPrev');
const btnNext = document.getElementById('btnNext');
const stepCounter = document.getElementById('stepCounter');
// ============ CONTENUS DES ÉTAPES ============
const stepContents = {
1: `
<h4><i class="bi bi-cart3 me-2 text-primary"></i>Votre Panier</h4>
<p>Vérifiez les articles avant de continuer.</p>
<div class="cart-item">
<img src="https://placehold.co/60x60/e9ecef/495057?text=S1" alt="Produit 1" class="cart-item-img">
<div class="cart-item-info">
<div class="cart-item-title">Chaussures Sportswear Air Max</div>
<div class="cart-item-meta">Taille : 42 • Couleur : Noir/Blanc</div>
</div>
<div class="cart-item-price">129,99 €</div>
</div>
<div class="cart-item">
<img src="https://placehold.co/60x60/e9ecef/495057?text=S2" alt="Produit 2" class="cart-item-img">
<div class="cart-item-info">
<div class="cart-item-title">Montre Connectée FitPro X200</div>
<div class="cart-item-meta">Bracelet silicone • Noir</div>
</div>
<div class="cart-item-price">249,99 €</div>
</div>
<hr class="my-3">
<div class="d-flex justify-content-between fw-bold">
<span>Total (2 articles)</span>
<span class="text-primary">379,98 €</span>
</div>
`,
2: `
<h4><i class="bi bi-truck me-2 text-primary"></i>Adresse de Livraison</h4>
<p>Choisissez votre adresse et le mode de livraison.</p>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-semibold">Prénom</label>
<input type="text" class="form-control rounded-3" placeholder="Jean" value="Thomas">
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Nom</label>
<input type="text" class="form-control rounded-3" placeholder="Dupont" value="Martin">
</div>
<div class="col-12">
<label class="form-label fw-semibold">Adresse</label>
<input type="text" class="form-control rounded-3" placeholder="123 rue Example" value="15 Avenue des Champs-Élysées">
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Code Postal</label>
<input type="text" class="form-control rounded-3" placeholder="75000" value="75008">
</div>
<div class="col-md-8">
<label class="form-label fw-semibold">Ville</label>
<input type="text" class="form-control rounded-3" placeholder="Paris" value="Paris">
</div>
<div class="col-12">
<label class="form-label fw-semibold">Mode de livraison</label>
<div class="form-check p-3 border rounded-3 mb-2 bg-white">
<input class="form-check-input" type="radio" name="delivery" id="standard" checked>
<label class="form-check-label fw-semibold" for="standard">
Standard — <span class="text-muted">3-5 jours ouvrés</span>
<br><small class="text-success">Gratuit</small>
</label>
</div>
<div class="form-check p-3 border rounded-3 bg-white">
<input class="form-check-input" type="radio" name="delivery" id="express">
<label class="form-check-label fw-semibold" for="express">
Express — <span class="text-muted">1-2 jours ouvrés</span>
<br><small class="text-muted">+ 9,99 €</small>
</label>
</div>
</div>
</div>
`,
3: `
<h4><i class="bi bi-credit-card me-2 text-primary"></i>Paiement Sécurisé</h4>
<p>Choisissez votre méthode de paiement.</p>
<div class="mb-4">
<div class="form-check p-3 border rounded-3 mb-2 bg-white">
<input class="form-check-input" type="radio" name="payment" id="card" checked>
<label class="form-check-label fw-semibold" for="card">
<i class="bi bi-credit-card-2-front me-2"></i>Carte Bancaire
</label>
</div>
<div class="form-check p-3 border rounded-3 mb-2 bg-white">
<input class="form-check-input" type="radio" name="payment" id="paypal">
<label class="form-check-label fw-semibold" for="paypal">
<i class="bi bi-paypal me-2"></i>PayPal
</label>
</div>
<div class="form-check p-3 border rounded-3 bg-white">
<input class="form-check-input" type="radio" name="payment" id="applepay">
<label class="form-check-label fw-semibold" for="applepay">
<i class="bi bi-apple me-2"></i>Apple Pay
</label>
</div>
</div>
<div class="bg-white p-3 rounded-3 border">
<div class="summary-line"><span>Sous-total</span><span>379,98 €</span></div>
<div class="summary-line"><span>Livraison</span><span class="text-success">Gratuite</span></div>
<div class="summary-line"><span>TVA (20%)</span><span>63,33 €</span></div>
<div class="summary-line summary-total mt-2 pt-2"><span>Total</span><span class="text-primary">379,98 €</span></div>
</div>
`
};
// ============ FONCTIONS ============
function updateStepsUI() {
// Mise à jour des classes des étapes
stepItems.forEach((item, index) => {
const stepNum = index + 1;
item.classList.remove('active', 'completed');
if (stepNum < currentStep || completedSteps.has(stepNum)) {
item.classList.add('completed');
} else if (stepNum === currentStep) {
item.classList.add('active');
}
});
// Mise à jour de la barre de progression
let progressPercent = 0;
if (currentStep === 2) progressPercent = 50;
if (currentStep === 3) progressPercent = 100;
stepsLineProgress.style.width = progressPercent + '%';
// Mise à jour du contenu
stepContent.innerHTML = stepContents[currentStep];
// Mise à jour des boutons
btnPrev.disabled = (currentStep === 1);
btnPrev.style.opacity = currentStep === 1 ? '0.5' : '1';
btnPrev.style.cursor = currentStep === 1 ? 'not-allowed' : 'pointer';
if (currentStep === TOTAL_STEPS) {
btnNext.style.display = 'none';
// Ajouter un bouton de confirmation
if (!document.getElementById('btnConfirm')) {
const confirmBtn = document.createElement('button');
confirmBtn.id = 'btnConfirm';
confirmBtn.className = 'btn btn-success-checkout';
confirmBtn.innerHTML = '<i class="bi bi-check-circle me-1"></i>Confirmer la commande';
confirmBtn.onclick = confirmOrder;
btnNext.parentNode.insertBefore(confirmBtn, btnNext.nextSibling);
}
} else {
btnNext.style.display = 'inline-block';
btnNext.innerHTML = 'Suivant<i class="bi bi-arrow-right ms-1"></i>';
const confirmBtn = document.getElementById('btnConfirm');
if (confirmBtn) confirmBtn.remove();
}
// Mise à jour du compteur
stepCounter.textContent = `Étape ${currentStep}/${TOTAL_STEPS}`;
}
function goToStep(step) {
// On peut toujours revenir en arrière
if (step < currentStep) {
currentStep = step;
updateStepsUI();
return;
}
// Pour avancer, il faut que l'étape précédente soit complétée
if (step > currentStep) {
// Vérifier si on peut avancer (l'étape actuelle est complétée ou on avance d'une seule étape)
if (step === currentStep + 1) {
// Marquer l'étape actuelle comme complétée avant d'avancer
completedSteps.add(currentStep);
currentStep = step;
updateStepsUI();
} else if (completedSteps.has(step - 1)) {
// L'étape précédente est complétée, on peut y aller
currentStep = step;
updateStepsUI();
}
// Sinon, on ignore le clic (l'utilisateur doit suivre l'ordre)
}
}
function nextStep() {
if (currentStep < TOTAL_STEPS) {
completedSteps.add(currentStep);
currentStep++;
updateStepsUI();
}
}
function prevStep() {
if (currentStep > 1) {
currentStep--;
updateStepsUI();
}
}
function confirmOrder() {
// Animation de confirmation
const confirmBtn = document.getElementById('btnConfirm');
if (confirmBtn) {
confirmBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Traitement...';
confirmBtn.disabled = true;
setTimeout(() => {
// Marquer l'étape 3 comme complétée
completedSteps.add(3);
updateStepsUI();
// Afficher un message de succès
stepContent.innerHTML = `
<div class="text-center py-4">
<div class="mb-3">
<i class="bi bi-check-circle-fill text-success" style="font-size: 4rem;"></i>
</div>
<h4 class="fw-bold">Commande confirmée !</h4>
<p class="text-muted mb-1">Votre commande #CMD-2026-0421 a bien été enregistrée.</p>
<p class="text-muted small">Un email de confirmation a été envoyé à thomas.martin@email.fr</p>
<button class="btn btn-primary rounded-3 mt-3" onclick="window.location.reload()">
<i class="bi bi-arrow-repeat me-1"></i>Nouvelle commande
</button>
</div>
`;
// Cacher les boutons de navigation
document.getElementById('navigationButtons').style.display = 'none';
// Marquer toutes les étapes comme complétées
stepItems.forEach(item => item.classList.add('completed'));
stepsLineProgress.style.width = '100%';
}, 1500);
}
}
// ============ RENDU GLOBAL DES FONCTIONS ============
window.goToStep = goToStep;
window.nextStep = nextStep;
window.prevStep = prevStep;
window.confirmOrder = confirmOrder;
// ============ INITIALISATION ============
updateStepsUI();
console.log('✅ CheckoutSteps initialisé • Étape actuelle :', currentStep);
})();
</script>
</body>
</html>
Télécharger le fichier source