Это компонент пользовательского интерфейса многошаговой формы (формата мастера), используемый на экранах со многими элементами ввода, такими как регистрация пользователя и анкеты. Он оснащен индикатором прогресса, который визуально отображает ваш прогресс на каждом этапе, и плавной анимацией скольжения.
Please enter the information used for login.
Please tell us your basic information.
You will be registered with the following information. Is this okay?
##Ключевые моменты дизайна и UX (Design&UX)
<div class="msf-step-indicator">...</div> и <div class="msf-step-content">...</div> в HTML и комбинируя их с conststeps = [1, 2, 3]; в JavaScript.btnNext.addEventListener) и добавляя элемент управления для выполнения currentStep++, если нет ошибок, его можно использовать в реальной работе.<div class="multi-step-form-wrapper bg-gray-50 dark:bg-zinc-950 flex items-center justify-center p-6 min-h-[600px] font-sans">
<div class="bg-white dark:bg-zinc-900 rounded-2xl shadow-xl w-full max-w-lg p-8 relative overflow-hidden border border-gray-100 dark:border-zinc-800">
<!-- Progress Bar -->
<div class="flex justify-between items-center mb-10 relative">
<div class="absolute left-0 top-1/2 transform -translate-y-1/2 w-full h-1.5 bg-gray-200 dark:bg-zinc-800 z-0 rounded-full"></div>
<div id="msf-progress-bar" class="absolute left-0 top-1/2 transform -translate-y-1/2 h-1.5 bg-indigo-600 z-0 transition-all duration-500 ease-out rounded-full" style="width: 0%;"></div>
<!-- Step 1 -->
<div class="relative z-10 flex flex-col items-center">
<div class="msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-indigo-600 text-white shadow-md border-4 border-white dark:border-zinc-900 ring-4 ring-indigo-100" data-step="1">1</div>
<span class="msf-step-label text-xs font-bold text-indigo-600 mt-2 transition-colors duration-300 tracking-wide uppercase">Account</span>
</div>
<!-- Step 2 -->
<div class="relative z-10 flex flex-col items-center">
<div class="msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-gray-200 dark:bg-zinc-800 text-gray-500 dark:text-gray-400 dark:text-gray-500 border-4 border-white dark:border-zinc-900" data-step="2">2</div>
<span class="msf-step-label text-xs font-medium text-gray-400 dark:text-gray-500 mt-2 transition-colors duration-300 tracking-wide uppercase">Personal</span>
</div>
<!-- Step 3 -->
<div class="relative z-10 flex flex-col items-center">
<div class="msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-gray-200 dark:bg-zinc-800 text-gray-500 dark:text-gray-400 dark:text-gray-500 border-4 border-white dark:border-zinc-900" data-step="3">3</div>
<span class="msf-step-label text-xs font-medium text-gray-400 dark:text-gray-500 mt-2 transition-colors duration-300 tracking-wide uppercase">Review</span>
</div>
</div>
<!-- Form Steps -->
<div class="relative h-[280px] overflow-visible">
<!-- Step 1 Content -->
<div class="msf-step-content absolute w-full transition-all duration-500 transform translate-x-0 opacity-100" data-step="1">
<h2 class="text-2xl font-extrabold text-gray-900 dark:text-white mb-2">Account Information</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 dark:text-gray-500 mb-6">Please enter the information used for login.</p>
<div class="space-y-4">
<div>
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Email Address</label>
<input type="email" required class="w-full text-gray-900 dark:text-white placeholder-gray-400 px-4 py-2.5 border border-gray-300 dark:border-zinc-700 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all shadow-sm bg-gray-50 dark:bg-zinc-950 focus:bg-white dark:bg-zinc-900" placeholder="you@example.com" />
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Password</label>
<input type="password" required class="w-full text-gray-900 dark:text-white placeholder-gray-400 px-4 py-2.5 border border-gray-300 dark:border-zinc-700 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all shadow-sm bg-gray-50 dark:bg-zinc-950 focus:bg-white dark:bg-zinc-900" placeholder="••••••••" />
</div>
</div>
</div>
<!-- Step 2 Content -->
<div class="msf-step-content absolute w-full transition-all duration-500 transform translate-x-12 opacity-0 pointer-events-none" data-step="2">
<h2 class="text-2xl font-extrabold text-gray-900 dark:text-white mb-2">Personal Information</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 dark:text-gray-500 mb-6">Please tell us your basic information.</p>
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Last Name</label>
<input type="text" required class="w-full text-gray-900 dark:text-white placeholder-gray-400 px-4 py-2.5 border border-gray-300 dark:border-zinc-700 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all shadow-sm bg-gray-50 dark:bg-zinc-950 focus:bg-white dark:bg-zinc-900" placeholder="Doe" />
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">First Name</label>
<input type="text" required class="w-full text-gray-900 dark:text-white placeholder-gray-400 px-4 py-2.5 border border-gray-300 dark:border-zinc-700 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all shadow-sm bg-gray-50 dark:bg-zinc-950 focus:bg-white dark:bg-zinc-900" placeholder="John" />
</div>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Phone Number</label>
<input type="tel" required class="w-full text-gray-900 dark:text-white placeholder-gray-400 px-4 py-2.5 border border-gray-300 dark:border-zinc-700 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition-all shadow-sm bg-gray-50 dark:bg-zinc-950 focus:bg-white dark:bg-zinc-900" placeholder="090-1234-5678" />
</div>
</div>
</div>
<!-- Step 3 Content -->
<div class="msf-step-content absolute w-full transition-all duration-500 transform translate-x-12 opacity-0 pointer-events-none" data-step="3">
<h2 class="text-2xl font-extrabold text-gray-900 dark:text-white mb-2">Confirm your input</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 dark:text-gray-500 mb-6">You will be registered with the following information. Is this okay?</p>
<div class="bg-gray-50 dark:bg-zinc-950/80 backdrop-blur-sm rounded-xl p-5 border border-gray-100 dark:border-zinc-800 shadow-inner space-y-4 text-sm">
<div class="flex justify-between items-center border-b border-gray-200 dark:border-zinc-800 pb-3">
<span class="text-gray-500 dark:text-gray-400 dark:text-gray-500 font-medium">Email Address</span>
<span class="font-bold text-gray-800 dark:text-gray-200">you@example.com</span>
</div>
<div class="flex justify-between items-center border-b border-gray-200 dark:border-zinc-800 pb-3">
<span class="text-gray-500 dark:text-gray-400 dark:text-gray-500 font-medium">氏First Name</span>
<span class="font-bold text-gray-800 dark:text-gray-200">John Doe</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500 dark:text-gray-400 dark:text-gray-500 font-medium">Phone Number</span>
<span class="font-bold text-gray-800 dark:text-gray-200">090-1234-5678</span>
</div>
</div>
</div>
</div>
<!-- Buttons -->
<div class="flex justify-between mt-8 pt-6 border-t border-gray-100 dark:border-zinc-800 relative z-10">
<button id="msf-btn-prev" class="px-6 py-2.5 border border-gray-300 dark:border-zinc-700 text-gray-700 dark:text-gray-300 font-bold rounded-xl hover:bg-gray-50 dark:bg-zinc-950 hover:text-indigo-600 focus:outline-none focus:ring-2 focus:ring-gray-200 transition-all duration-300 opacity-0 pointer-events-none disabled:opacity-50 flex items-center justify-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Back
</button>
<button id="msf-btn-next" class="px-6 py-2.5 bg-indigo-600 text-white font-bold rounded-xl hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-300 ml-auto shadow-[0_4px_14px_0_rgba(79,70,229,0.39)] hover:shadow-[0_6px_20px_rgba(79,70,229,0.23)] hover:-translate-y-0.5 flex items-center justify-center gap-2 active:translate-y-0">
Next
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
</button>
</div>
</div>
</div>
<script>
(function() {
const steps = [1, 2, 3];
let currentStep = 1;
const progressBar = document.getElementById('msf-progress-bar');
const indicators = document.querySelectorAll('.msf-step-indicator');
const labels = document.querySelectorAll('.msf-step-label');
const contents = document.querySelectorAll('.msf-step-content');
const btnPrev = document.getElementById('msf-btn-prev');
const btnNext = document.getElementById('msf-btn-next');
if (!progressBar) return;
function validateStep() {
if (currentStep === steps.length) return true;
const currentContent = document.querySelector('.msf-step-content[data-step="' + currentStep + '"]');
const inputs = currentContent.querySelectorAll('input[required]');
let isValid = true;
inputs.forEach(input => {
if (!input.checkValidity()) {
input.reportValidity();
isValid = false;
}
});
return isValid;
}
function updateUI() {
const progressPercentage = ((currentStep - 1) / (steps.length - 1)) * 100;
progressBar.style.width = progressPercentage + '%';
indicators.forEach((ind, index) => {
const stepNum = index + 1;
const label = labels[index];
if (stepNum < currentStep) {
ind.className = "msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-indigo-600 text-white shadow-md border-4 border-white dark:border-zinc-900";
ind.innerHTML = '<svg class="w-5 h-5 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"></path></svg>';
label.className = "msf-step-label text-xs font-bold text-indigo-600 mt-2 transition-colors duration-300 tracking-wide uppercase";
} else if (stepNum === currentStep) {
ind.className = "msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-indigo-600 text-white shadow-md border-4 border-white dark:border-zinc-900 ring-4 ring-indigo-100 scale-110";
ind.innerHTML = stepNum;
label.className = "msf-step-label text-xs font-bold text-indigo-600 mt-2 transition-colors duration-300 tracking-wide uppercase";
} else {
ind.className = "msf-step-indicator w-10 h-10 rounded-full flex items-center justify-center font-bold transition-all duration-500 bg-gray-200 dark:bg-zinc-800 text-gray-500 dark:text-gray-400 dark:text-gray-500 border-4 border-white dark:border-zinc-900";
ind.innerHTML = stepNum;
label.className = "msf-step-label text-xs font-medium text-gray-400 dark:text-gray-500 mt-2 transition-colors duration-300 tracking-wide uppercase";
}
});
contents.forEach((content, index) => {
const stepNum = index + 1;
if (stepNum === currentStep) {
content.className = "msf-step-content absolute w-full transition-all duration-500 transform translate-x-0 opacity-100 delay-100";
} else if (stepNum < currentStep) {
content.className = "msf-step-content absolute w-full transition-all duration-500 transform -translate-x-12 opacity-0 pointer-events-none";
} else {
content.className = "msf-step-content absolute w-full transition-all duration-500 transform translate-x-12 opacity-0 pointer-events-none";
}
});
if (currentStep === 1) {
btnPrev.className = "px-6 py-2.5 border border-gray-300 dark:border-zinc-700 text-gray-700 dark:text-gray-300 font-bold rounded-xl transition-all duration-300 opacity-0 pointer-events-none absolute left-0";
} else {
btnPrev.className = "px-6 py-2.5 border border-gray-300 dark:border-zinc-700 text-gray-700 dark:text-gray-300 font-bold rounded-xl hover:bg-gray-50 dark:bg-zinc-950 hover:text-indigo-600 focus:outline-none focus:ring-2 focus:ring-gray-200 transition-all duration-300 opacity-100 flex items-center justify-center gap-2";
}
if (currentStep === steps.length) {
btnNext.innerHTML = 'Submit <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>';
btnNext.className = "px-6 py-2.5 bg-emerald-500 text-white font-bold rounded-xl hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2 transition-all duration-300 ml-auto shadow-[0_4px_14px_0_rgba(16,185,129,0.39)] hover:shadow-[0_6px_20px_rgba(16,185,129,0.23)] hover:-translate-y-0.5 flex items-center justify-center gap-2 active:translate-y-0";
} else {
btnNext.innerHTML = 'Next <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>';
btnNext.className = "px-6 py-2.5 bg-indigo-600 text-white font-bold rounded-xl hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-300 ml-auto shadow-[0_4px_14px_0_rgba(79,70,229,0.39)] hover:shadow-[0_6px_20px_rgba(79,70,229,0.23)] hover:-translate-y-0.5 flex items-center justify-center gap-2 active:translate-y-0";
}
}
btnPrev.addEventListener('click', () => {
if (currentStep > 1) {
currentStep--;
updateUI();
}
});
btnNext.addEventListener('click', () => {
if (!validateStep()) return;
if (currentStep < steps.length) {
currentStep++;
updateUI();
} else {
btnNext.innerHTML = '<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Submitting...';
setTimeout(() => {
alert('Form submitted successfully!');
currentStep = 1;
updateUI();
}, 1500);
}
});
updateUI();
})();
</script> Прекрасно работает с последними версиями современных браузеров (Chrome, Safari, Firefox, Edge).Служебные классы CSS Transitions и Tailwind используются для анимации, а JavaScript выполняет управление состоянием (только замена классов и вычисление ширины индикатора выполнения), что делает его легким и быстрым.