Html
Css
Javascript
Checkout
Stepper
E Commerce
Multi Etapes
Template
Processus de commande multi-étapes en HTML CSS JS pur : stepper sans dépendance, transitions fluides, panier dynamique et confirmation animée.
<!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 HTML CSS JS 2026 05020023 | AngularForAll</title>
<style>
/* =============================================
RESET & VARIABLES
============================================= */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-primary: #5b5fef;
--color-primary-light: #eeefff;
--color-primary-dark: #4845d2;
--color-success: #2dd4a8;
--color-success-light: #e6faf4;
--color-warning: #f59e0b;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-white: #ffffff;
--color-red-400: #f87171;
--color-red-500: #ef4444;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.08), 0 4px 10px rgba(0, 0, 0, 0.05);
--shadow-glow: 0 0 0 8px rgba(91, 95, 239, 0.12), 0 4px 16px rgba(91, 95, 239, 0.18);
--shadow-success-glow: 0 0 0 8px rgba(45, 212, 168, 0.1), 0 4px 12px rgba(45, 212, 168, 0.15);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-2xl: 24px;
--transition-fast: 0.2s ease;
--transition-smooth: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
--transition-bounce: 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* =============================================
BASE
============================================= */
body {
font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
background: linear-gradient(160deg, #f8f9fc 0%, #eef0f8 40%, #f5f3ff 100%);
background-attachment: fixed;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: var(--color-gray-800);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Motif de fond subtil */
body::before {
content: '';
position: fixed;
inset: 0;
background-image:
radial-gradient(circle at 15% 20%, rgba(91, 95, 239, 0.04) 0%, transparent 50%),
radial-gradient(circle at 85% 75%, rgba(45, 212, 168, 0.04) 0%, transparent 50%),
radial-gradient(circle at 50% 50%, rgba(245, 158, 11, 0.02) 0%, transparent 60%);
pointer-events: none;
z-index: 0;
}
/* =============================================
CONTENEUR PRINCIPAL
============================================= */
.checkout-wrapper {
position: relative;
z-index: 1;
width: 100%;
max-width: 660px;
}
.checkout-card {
background: var(--color-white);
border-radius: var(--radius-2xl);
box-shadow: var(--shadow-lg);
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.04);
}
/* =============================================
EN-TÊTE
============================================= */
.checkout-header {
background: linear-gradient(135deg, #5b5fef 0%, #6c63ff 50%, #4845d2 100%);
padding: 28px 32px;
color: white;
position: relative;
overflow: hidden;
}
.checkout-header::before {
content: '';
position: absolute;
top: -50%;
right: -20%;
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);
border-radius: 50%;
}
.checkout-header::after {
content: '';
position: absolute;
bottom: -30%;
left: 40%;
width: 150px;
height: 150px;
background: radial-gradient(circle, rgba(255,255,255,0.05) 0%, transparent 70%);
border-radius: 50%;
}
.header-content {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 14px;
}
.header-icon {
width: 44px;
height: 44px;
background: rgba(255, 255, 255, 0.18);
backdrop-filter: blur(4px);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.header-text h1 {
font-size: 1.35rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 2px;
}
.header-text p {
font-size: 0.8rem;
opacity: 0.7;
letter-spacing: 0.01em;
}
/* =============================================
CORPS
============================================= */
.checkout-body {
padding: 28px 32px 32px;
}
/* =============================================
INDICATEUR D'ÉTAPES
============================================= */
.steps-container {
position: relative;
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 32px;
padding: 0 8px;
}
/* Ligne de connexion */
.steps-connector {
position: absolute;
top: 27px;
left: 42px;
right: 42px;
height: 3px;
background: var(--color-gray-200);
border-radius: 3px;
z-index: 0;
}
.steps-connector-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary), var(--color-success));
border-radius: 3px;
transition: width var(--transition-smooth);
width: 0%;
}
/* Étape individuelle */
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 2;
cursor: pointer;
flex: 1;
transition: transform var(--transition-fast);
}
.step:hover {
transform: translateY(-2px);
}
.step-circle {
width: 54px;
height: 54px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.15rem;
font-weight: 700;
transition: all var(--transition-smooth);
position: relative;
background: var(--color-gray-200);
color: var(--color-gray-500);
border: 3px solid transparent;
margin-bottom: 10px;
flex-shrink: 0;
}
/* État actif */
.step.active .step-circle {
background: var(--color-white);
color: var(--color-primary);
border-color: var(--color-primary);
box-shadow: var(--shadow-glow);
transform: scale(1.06);
}
/* État complété */
.step.completed .step-circle {
background: var(--color-success);
color: var(--color-white);
border-color: var(--color-success);
box-shadow: var(--shadow-success-glow);
}
.step-number {
transition: opacity var(--transition-fast);
}
.step-check {
display: none;
font-size: 1.3rem;
}
.step.completed .step-number {
display: none;
}
.step.completed .step-check {
display: inline;
}
/* Labels */
.step-label {
font-size: 0.82rem;
font-weight: 600;
color: var(--color-gray-500);
text-transform: uppercase;
letter-spacing: 0.04em;
transition: color var(--transition-smooth);
text-align: center;
display: flex;
align-items: center;
gap: 4px;
}
.step.active .step-label {
color: var(--color-primary);
}
.step.completed .step-label {
color: var(--color-success);
}
.step-sublabel {
font-size: 0.68rem;
color: var(--color-gray-400);
margin-top: 2px;
text-align: center;
transition: color var(--transition-smooth);
}
.step.active .step-sublabel {
color: var(--color-primary);
opacity: 0.7;
}
.step.completed .step-sublabel {
color: var(--color-success);
opacity: 0.7;
}
/* =============================================
CONTENU DES ÉTAPES
============================================= */
.step-content {
min-height: 260px;
animation: fadeSlideIn 0.35s ease forwards;
background: var(--color-gray-50);
border-radius: var(--radius-xl);
padding: 24px;
border: 1px solid var(--color-gray-100);
}
@keyframes fadeSlideIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.content-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.content-icon {
width: 40px;
height: 40px;
background: var(--color-primary-light);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
color: var(--color-primary);
flex-shrink: 0;
}
.content-title h3 {
font-size: 1.1rem;
font-weight: 700;
color: var(--color-gray-800);
margin-bottom: 2px;
}
.content-title p {
font-size: 0.8rem;
color: var(--color-gray-400);
}
/* =============================================
ÉLÉMENTS DU PANIER
============================================= */
.cart-items {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 16px;
}
.cart-item {
display: flex;
align-items: center;
gap: 14px;
padding: 14px;
background: var(--color-white);
border-radius: var(--radius-lg);
border: 1px solid var(--color-gray-100);
transition: all var(--transition-fast);
}
.cart-item:hover {
border-color: var(--color-gray-200);
box-shadow: var(--shadow-sm);
}
.cart-item-image {
width: 50px;
height: 50px;
background: var(--color-gray-100);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: var(--color-gray-400);
flex-shrink: 0;
}
.cart-item-info {
flex: 1;
min-width: 0;
}
.cart-item-name {
font-weight: 600;
font-size: 0.9rem;
color: var(--color-gray-800);
margin-bottom: 2px;
}
.cart-item-detail {
font-size: 0.75rem;
color: var(--color-gray-400);
}
.cart-item-price {
font-weight: 700;
font-size: 0.95rem;
color: var(--color-gray-800);
flex-shrink: 0;
}
.cart-item-remove {
background: none;
border: none;
color: var(--color-gray-300);
cursor: pointer;
font-size: 16px;
padding: 4px;
transition: color var(--transition-fast);
flex-shrink: 0;
}
.cart-item-remove:hover {
color: var(--color-red-400);
}
.cart-total {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
background: var(--color-primary-light);
border-radius: var(--radius-lg);
}
.cart-total-label {
font-size: 0.85rem;
color: var(--color-gray-600);
}
.cart-total-price {
font-weight: 700;
font-size: 1.2rem;
color: var(--color-primary);
}
/* =============================================
FORMULAIRES
============================================= */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 16px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-group.full {
grid-column: 1 / -1;
}
.form-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-gray-500);
}
.form-input {
padding: 10px 14px;
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-md);
font-size: 0.9rem;
font-family: inherit;
color: var(--color-gray-800);
background: var(--color-white);
transition: all var(--transition-fast);
outline: none;
width: 100%;
}
.form-input:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(91, 95, 239, 0.1);
}
/* Options de sélection */
.options-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.option-card {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-lg);
cursor: pointer;
transition: all var(--transition-fast);
background: var(--color-white);
}
.option-card:hover {
border-color: var(--color-gray-300);
}
.option-card.selected {
border-color: var(--color-primary);
background: var(--color-primary-light);
}
.option-radio {
width: 18px;
height: 18px;
accent-color: var(--color-primary);
cursor: pointer;
}
.option-info {
flex: 1;
}
.option-title {
font-weight: 600;
font-size: 0.9rem;
color: var(--color-gray-800);
}
.option-subtitle {
font-size: 0.75rem;
color: var(--color-gray-400);
}
.option-badge {
font-size: 0.75rem;
font-weight: 600;
padding: 4px 10px;
border-radius: 20px;
background: var(--color-success-light);
color: var(--color-success);
}
/* =============================================
RÉSUMÉ PAIEMENT
============================================= */
.summary-card {
background: var(--color-white);
border-radius: var(--radius-lg);
padding: 16px;
border: 1px solid var(--color-gray-100);
}
.summary-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
font-size: 0.88rem;
color: var(--color-gray-600);
}
.summary-row:not(:last-child) {
border-bottom: 1px solid var(--color-gray-100);
}
.summary-row.total {
font-weight: 700;
font-size: 1.05rem;
color: var(--color-gray-800);
border-bottom: none;
padding-top: 12px;
}
.summary-row .highlight {
color: var(--color-primary);
font-weight: 700;
}
.text-green {
color: var(--color-success);
font-weight: 600;
}
/* =============================================
BOUTONS
============================================= */
.nav-buttons {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid var(--color-gray-100);
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 11px 22px;
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all var(--transition-fast);
border: 2px solid transparent;
outline: none;
white-space: nowrap;
}
.btn:active {
transform: scale(0.96);
}
.btn-prev {
background: var(--color-white);
border-color: var(--color-gray-200);
color: var(--color-gray-600);
}
.btn-prev:hover:not(:disabled) {
background: var(--color-gray-50);
border-color: var(--color-gray-300);
}
.btn-prev:disabled {
opacity: 0.4;
cursor: not-allowed;
background: var(--color-gray-100);
color: var(--color-gray-400);
}
.btn-next {
background: var(--color-primary);
color: white;
border-color: var(--color-primary);
box-shadow: 0 4px 14px rgba(91, 95, 239, 0.3);
}
.btn-next:hover {
background: var(--color-primary-dark);
border-color: var(--color-primary-dark);
box-shadow: 0 6px 18px rgba(91, 95, 239, 0.35);
}
.btn-confirm {
background: var(--color-success);
color: white;
border-color: var(--color-success);
box-shadow: 0 4px 14px rgba(45, 212, 168, 0.3);
font-size: 0.9rem;
}
.btn-confirm:hover {
background: #25bf97;
border-color: #25bf97;
box-shadow: 0 6px 18px rgba(45, 212, 168, 0.35);
}
.step-counter {
font-size: 0.8rem;
color: var(--color-gray-400);
font-weight: 500;
}
/* =============================================
ÉTAT DE SUCCÈS
============================================= */
.success-state {
text-align: center;
padding: 30px 20px;
}
.success-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 72px;
height: 72px;
background: var(--color-success-light);
border-radius: 50%;
font-size: 36px;
color: var(--color-success);
margin-bottom: 20px;
animation: bounceIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes bounceIn {
0% { opacity: 0; transform: scale(0.7); }
50% { transform: scale(1.08); }
100% { opacity: 1; transform: scale(1); }
}
.success-title {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-gray-800);
margin-bottom: 8px;
}
.success-message {
font-size: 0.9rem;
color: var(--color-gray-500);
margin-bottom: 6px;
}
.success-order-id {
font-weight: 700;
color: var(--color-gray-700);
}
.success-email {
font-size: 0.8rem;
color: var(--color-gray-400);
margin-bottom: 20px;
}
.btn-restart {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 11px 22px;
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
background: var(--color-primary);
color: white;
border: 2px solid var(--color-primary);
box-shadow: 0 4px 14px rgba(91, 95, 239, 0.3);
transition: all var(--transition-fast);
}
.btn-restart:hover {
background: var(--color-primary-dark);
box-shadow: 0 6px 18px rgba(91, 95, 239, 0.35);
}
/* =============================================
RESPONSIVE
============================================= */
@media (max-width: 600px) {
.checkout-header {
padding: 22px 20px;
}
.checkout-body {
padding: 20px 16px 24px;
}
.step-circle {
width: 42px;
height: 42px;
font-size: 0.95rem;
}
.steps-connector {
top: 21px;
left: 28px;
right: 28px;
}
.step-label {
font-size: 0.7rem;
}
.step-sublabel {
font-size: 0.6rem;
}
.form-grid {
grid-template-columns: 1fr;
}
.nav-buttons {
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.step-counter {
order: 3;
width: 100%;
text-align: center;
}
.option-card {
padding: 12px;
}
.cart-item {
flex-wrap: wrap;
gap: 8px;
}
}
@media (max-width: 380px) {
.steps-container {
flex-direction: column;
align-items: flex-start;
gap: 20px;
padding-left: 16px;
}
.step {
flex-direction: row;
gap: 14px;
flex: none;
}
.step-circle {
margin-bottom: 0;
}
.steps-connector {
display: none;
}
/* Ligne verticale entre les étapes */
.step:not(:last-child)::after {
content: '';
position: absolute;
left: 21px;
top: 44px;
width: 3px;
height: 24px;
background: var(--color-gray-200);
z-index: 0;
}
.step.completed:not(:last-child)::after {
background: var(--color-success);
}
.step.active:not(:last-child)::after {
background: linear-gradient(to bottom, var(--color-primary), var(--color-gray-200));
}
}
</style>
</head>
<body>
<div class="checkout-wrapper">
<div class="checkout-card">
<!-- ========== EN-TÊTE ========== -->
<div class="checkout-header">
<div class="header-content">
<div class="header-icon">🛒</div>
<div class="header-text">
<h1>Finaliser la commande</h1>
<p>Complétez les étapes ci-dessous</p>
</div>
</div>
</div>
<!-- ========== CORPS ========== -->
<div class="checkout-body">
<!-- ========== INDICATEUR D'ÉTAPES ========== -->
<div class="steps-container" id="stepsContainer">
<div class="steps-connector">
<div class="steps-connector-fill" id="connectorFill"></div>
</div>
<!-- Étape 1 -->
<div class="step active" data-step="1" onclick="goToStep(1)">
<div class="step-circle">
<span class="step-number">1</span>
<span class="step-check">✓</span>
</div>
<div>
<div class="step-label">🛍️ Panier</div>
<div class="step-sublabel">Vérifier</div>
</div>
</div>
<!-- Étape 2 -->
<div class="step" data-step="2" onclick="goToStep(2)">
<div class="step-circle">
<span class="step-number">2</span>
<span class="step-check">✓</span>
</div>
<div>
<div class="step-label">🚚 Livraison</div>
<div class="step-sublabel">Adresse</div>
</div>
</div>
<!-- Étape 3 -->
<div class="step" data-step="3" onclick="goToStep(3)">
<div class="step-circle">
<span class="step-number">3</span>
<span class="step-check">✓</span>
</div>
<div>
<div class="step-label">💳 Paiement</div>
<div class="step-sublabel">Finaliser</div>
</div>
</div>
</div>
<!-- ========== CONTENU DYNAMIQUE ========== -->
<div class="step-content" id="stepContent">
<!-- Rempli par JavaScript -->
</div>
<!-- ========== BOUTONS DE NAVIGATION ========== -->
<div class="nav-buttons" id="navButtons">
<button class="btn btn-prev" id="btnPrev" onclick="previousStep()" disabled>
← Précédent
</button>
<span class="step-counter" id="stepCounter">Étape 1/3</span>
<button class="btn btn-next" id="btnNext" onclick="nextStep()">
Suivant →
</button>
</div>
</div>
</div>
</div>
<script>
(function() {
// ============ ÉTAT ============
const TOTAL_STEPS = 3;
let currentStep = 1;
const completedSteps = new Set();
// ============ ÉLÉMENTS DOM ============
const stepElements = document.querySelectorAll('.step');
const connectorFill = document.getElementById('connectorFill');
const stepContent = document.getElementById('stepContent');
const btnPrev = document.getElementById('btnPrev');
const btnNext = document.getElementById('btnNext');
const stepCounter = document.getElementById('stepCounter');
const navButtons = document.getElementById('navButtons');
// ============ CONTENUS DES ÉTAPES ============
function getStepContent(step) {
switch(step) {
case 1:
return `
<div class="content-header">
<div class="content-icon">🛍️</div>
<div class="content-title">
<h3>Votre Panier</h3>
<p>Vérifiez vos articles avant de continuer</p>
</div>
</div>
<div class="cart-items">
<div class="cart-item">
<div class="cart-item-image">👟</div>
<div class="cart-item-info">
<div class="cart-item-name">Chaussures Air Max Neon</div>
<div class="cart-item-detail">Taille 42 • Noir/Blanc</div>
</div>
<div class="cart-item-price">129,99 €</div>
<button class="cart-item-remove" title="Supprimer">✕</button>
</div>
<div class="cart-item">
<div class="cart-item-image">⌚</div>
<div class="cart-item-info">
<div class="cart-item-name">Montre FitPro X200</div>
<div class="cart-item-detail">Bracelet silicone • Noir</div>
</div>
<div class="cart-item-price">249,99 €</div>
<button class="cart-item-remove" title="Supprimer">✕</button>
</div>
</div>
<div class="cart-total">
<span class="cart-total-label">Total (2 articles)</span>
<span class="cart-total-price">379,98 €</span>
</div>
`;
case 2:
return `
<div class="content-header">
<div class="content-icon">🚚</div>
<div class="content-title">
<h3>Adresse de livraison</h3>
<p>Où souhaitez-vous recevoir votre commande ?</p>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label">Prénom</label>
<input type="text" class="form-input" value="Thomas" placeholder="Votre prénom">
</div>
<div class="form-group">
<label class="form-label">Nom</label>
<input type="text" class="form-input" value="Martin" placeholder="Votre nom">
</div>
<div class="form-group full">
<label class="form-label">Adresse</label>
<input type="text" class="form-input" value="15 Avenue des Champs-Élysées" placeholder="Numéro et rue">
</div>
<div class="form-group">
<label class="form-label">Code Postal</label>
<input type="text" class="form-input" value="75008" placeholder="75000">
</div>
<div class="form-group">
<label class="form-label">Ville</label>
<input type="text" class="form-input" value="Paris" placeholder="Ville">
</div>
</div>
<div class="options-list">
<label class="option-card selected">
<input type="radio" name="delivery" class="option-radio" checked>
<div class="option-info">
<div class="option-title">Standard</div>
<div class="option-subtitle">3-5 jours ouvrés</div>
</div>
<span class="option-badge">Gratuit</span>
</label>
<label class="option-card">
<input type="radio" name="delivery" class="option-radio">
<div class="option-info">
<div class="option-title">Express</div>
<div class="option-subtitle">1-2 jours ouvrés</div>
</div>
<span style="font-size:0.8rem;color:var(--color-gray-500);">+ 9,99 €</span>
</label>
</div>
`;
case 3:
return `
<div class="content-header">
<div class="content-icon">💳</div>
<div class="content-title">
<h3>Paiement sécurisé</h3>
<p>Choisissez votre méthode de paiement</p>
</div>
</div>
<div class="options-list" style="margin-bottom:16px;">
<label class="option-card selected">
<input type="radio" name="payment" class="option-radio" checked>
<div class="option-info">
<div class="option-title">💳 Carte Bancaire</div>
<div class="option-subtitle">Visa, Mastercard, CB</div>
</div>
</label>
<label class="option-card">
<input type="radio" name="payment" class="option-radio">
<div class="option-info">
<div class="option-title">🅿️ PayPal</div>
<div class="option-subtitle">Connexion sécurisée</div>
</div>
</label>
<label class="option-card">
<input type="radio" name="payment" class="option-radio">
<div class="option-info">
<div class="option-title">🍎 Apple Pay</div>
<div class="option-subtitle">Paiement express</div>
</div>
</label>
</div>
<div class="summary-card">
<div class="summary-row">
<span>Sous-total</span>
<span>379,98 €</span>
</div>
<div class="summary-row">
<span>Livraison</span>
<span class="text-green">Gratuite</span>
</div>
<div class="summary-row">
<span>TVA (20%)</span>
<span>63,33 €</span>
</div>
<div class="summary-row total">
<span>Total</span>
<span class="highlight">379,98 €</span>
</div>
</div>
`;
default:
return '';
}
}
// ============ MISE À JOUR DE L'INTERFACE ============
function updateUI() {
// Mise à jour des classes des étapes
stepElements.forEach((stepEl, index) => {
const stepNum = index + 1;
stepEl.classList.remove('active', 'completed');
if (completedSteps.has(stepNum)) {
stepEl.classList.add('completed');
} else if (stepNum === currentStep) {
stepEl.classList.add('active');
}
});
// Barre de progression
const progressPercent = currentStep === 1 ? 0 : currentStep === 2 ? 50 : 100;
connectorFill.style.width = progressPercent + '%';
// Contenu
stepContent.innerHTML = getStepContent(currentStep);
// Réattacher les événements des options cards
attachOptionCardEvents();
// Bouton précédent
if (currentStep === 1) {
btnPrev.disabled = true;
} else {
btnPrev.disabled = false;
}
// Bouton suivant
if (currentStep === TOTAL_STEPS) {
btnNext.textContent = '✓ Confirmer';
btnNext.className = 'btn btn-confirm';
btnNext.onclick = confirmOrder;
} else {
btnNext.textContent = 'Suivant →';
btnNext.className = 'btn btn-next';
btnNext.onclick = nextStep;
}
// Compteur
stepCounter.textContent = `Étape ${currentStep}/${TOTAL_STEPS}`;
}
// ============ ÉVÉNEMENTS DES OPTIONS ============
function attachOptionCardEvents() {
const optionCards = document.querySelectorAll('.option-card');
optionCards.forEach(card => {
card.addEventListener('click', function() {
// Désélectionner toutes les cartes du même groupe
const radio = this.querySelector('input[type="radio"]');
if (!radio) return;
const name = radio.name;
const allCards = document.querySelectorAll(`input[name="${name}"]`);
allCards.forEach(input => {
input.closest('.option-card').classList.remove('selected');
});
// Sélectionner la carte cliquée
this.classList.add('selected');
radio.checked = true;
});
});
}
// ============ NAVIGATION ============
function goToStep(step) {
if (step < currentStep || completedSteps.has(step - 1)) {
currentStep = step;
updateUI();
// Animation de feedback
pulseCurrentStep();
}
}
function nextStep() {
if (currentStep < TOTAL_STEPS) {
completedSteps.add(currentStep);
currentStep++;
updateUI();
pulseCurrentStep();
}
}
function previousStep() {
if (currentStep > 1) {
currentStep--;
updateUI();
}
}
function pulseCurrentStep() {
const activeStep = document.querySelector('.step.active .step-circle');
if (activeStep) {
activeStep.style.transform = 'scale(1.12)';
setTimeout(() => {
activeStep.style.transform = 'scale(1.06)';
}, 200);
}
}
function confirmOrder() {
// Marquer l'étape 3 comme complétée
completedSteps.add(3);
updateUI();
// Animation de chargement
btnNext.textContent = '⏳ Traitement...';
btnNext.disabled = true;
btnNext.style.opacity = '0.75';
btnNext.style.cursor = 'wait';
setTimeout(() => {
// Afficher l'état de succès
stepContent.innerHTML = `
<div class="success-state">
<div class="success-icon">✓</div>
<div class="success-title">Commande confirmée !</div>
<div class="success-message">
Votre commande <span class="success-order-id">#CMD-2026-0421</span> a bien été enregistrée.
</div>
<div class="success-email">Un email a été envoyé à thomas.martin@email.fr</div>
<button class="btn-restart" onclick="location.reload()">
↻ Nouvelle commande
</button>
</div>
`;
// Mettre à jour la barre de progression
connectorFill.style.width = '100%';
// Mettre toutes les étapes en complétées
stepElements.forEach(el => {
el.classList.remove('active');
el.classList.add('completed');
});
// Cacher les boutons de navigation
navButtons.style.display = 'none';
}, 1500);
}
// ============ FONCTIONS GLOBALES ============
window.goToStep = goToStep;
window.nextStep = nextStep;
window.previousStep = previousStep;
window.confirmOrder = confirmOrder;
// ============ INITIALISATION ============
updateUI();
attachOptionCardEvents();
console.log('✅ CheckoutSteps • Vanilla JS • Prêt à l\'emploi');
})();
</script>
</body>
</html>
Télécharger le fichier source