Creating a multistep form involves structuring your HTML for multiple steps, using CSS for styling, and JavaScript to handle the navigation between steps. Here’s a basic example to get you started:
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Multistep Form</title> <link rel="stylesheet" href="style.css" /> <script src="script.js" defer></script> </head> <body> <form class="form-wizard"> <!-- .completed --> <div class="completed" hidden> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" > <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /> </svg> <h3>Registration Successful!</h3> <p>Your account has been created.</p> </div> <!-- /.completed --> <h1>Registration</h1> <!-- .progress-container --> <div class="progress-container"> <div class="progress"></div> <ol> <li class="current">Personal Info</li> <li>Contact Info</li> <li>Account Info</li> <li>About</li> </ol> </div> <!-- /.progress-container --> <!-- .steps-container --> <div class="steps-container"> <div class="step"> <h3>Personal Information</h3> <div class="form-control"> <label for="first-name">First Name</label> <input type="text" id="first-name" name="first-name" required /> </div> <div class="form-control"> <label for="last-name">Last Name</label> <input type="text" id="last-name" name="last-name" required /> </div> </div> <div class="step"> <h3>Contact Information</h3> <div class="form-control"> <label for="email">Email</label> <input type="email" id="email" name="email" required /> </div> <div class="form-control"> <label for="phone">Phone</label> <input type="tel" id="phone" name="phone" required /> </div> </div> <div class="step"> <h3>Account Information</h3> <div class="form-control"> <label for="username">Username</label> <input type="text" id="username" name="username" required /> </div> <div class="form-control"> <label for="password">Password</label> <input type="password" id="password" name="password" required /> </div> <div class="form-control"> <label for="confirm-password">Confirm Password</label> <input type="password" id="confirm-password" name="confirm-password" required /> </div> </div> <div class="step"> <h3>About</h3> <div class="form-control"> <label for="bio">Bio</label> <textarea name="bio" id="bio" rows="4" required></textarea> </div> </div> </div> <!-- /.steps-container --> <!-- .controls --> <div class="controls"> <button type="button" class="prev-btn">Prev</button> <button type="button" class="next-btn">Next</button> <button type="submit" class="submit-btn">Submit</button> </div> <!-- /.controls --> </form> </body> </html>
CSS (styles.css)
@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600&display=swap"); * { font-family: "Outfit", sans-serif; padding: 0; margin: 0; box-sizing: border-box; } :root { --main-color: #c41e67; --dark-color: #a71555; --step-color: #ced7e0; --step-size: 32px; --steps: 4; --progress-width: calc((var(--steps) - 1) / var(--steps) * 100%); } body { height: 100vh; display: flex; align-items: center; justify-content: center; background-color: lavender; color: #333; overflow: hidden; } h1 { text-align: center; margin-bottom: 16px; } h3 { margin-bottom: 16px; } label { display: inline-block; margin-bottom: 4px; } input, textarea { outline-color: var(--main-color); border: 1px solid lightgray; padding: 8px 16px; border-radius: 4px; width: 100%; font-size: 16px; } textarea { resize: none; } button { cursor: pointer; border: none; border-radius: 16px; padding: 8px 16px; background-color: var(--main-color); color: #fff; font-size: 14px; transition: 0.3s; } button:active, button:hover { background-color: var(--dark-color); } button:disabled { opacity: 0.8; } button:focus { outline-color: #333; } button[type="submit"] { background-color: #333; } button[type="submit"]:hover, button[type="submit"]:active { background-color: #444; } /* form styles */ .form-wizard { background-color: #fff; padding: 32px; border-radius: 8px; width: min(600px, 80%); box-shadow: 0 4px 16px rgba(167, 21, 84, 0.1); } .progress-container { position: relative; } .progress-container::before { content: ""; height: 4px; width: var(--progress-width); background-color: var(--step-color); position: absolute; top: calc(var(--step-size) / 2); left: 50%; translate: -50% -50%; } .progress-container .progress { height: 4px; width: var(--progress-width); background-color: var(--main-color); position: absolute; top: calc(var(--step-size) / 2); left: 50%; translate: -50% -50%; z-index: 2; transform: scaleX(0); transform-origin: left; transition: 0.3s; } .progress-container ol { list-style-type: none; display: flex; align-items: flex-start; justify-content: space-between; counter-reset: step-number; position: relative; z-index: 3; } .progress-container li { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--step-color); transition: 0.3s linear 0.3s; text-align: center; } .progress-container li::before { counter-increment: step-number; content: counter(step-number); background-color: var(--step-color); width: var(--step-size); height: var(--step-size); display: flex; align-items: center; justify-content: center; border-radius: 50%; color: #fff; transition: 0.3s linear 0.3s; } .progress-container li:where(.done, .current) { color: #333; font-weight: 500; } .progress-container li.done::before { background-color: var(--main-color); } .progress-container li.current::before { background-color: var(--main-color); box-shadow: 0 0 0 3px rgba(167, 21, 84, 0.25); } .steps-container { display: flex; overflow: hidden; } .step { flex: 1 0 100%; padding: 24px 8px; opacity: 0; transition: opacity 0.3s; height: fit-content; display: grid; gap: 8px; } .step.current { opacity: 1; } .controls { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 16px; padding-inline: 8px; } .controls button { flex: 1; } .completed { text-align: center; } .completed h3 { margin-bottom: 8px; } .completed svg { width: 100px; height: 100px; stroke: yellowgreen; } .completed:not([hidden]) ~ * { display: none; }
JavaScript (script.js)
document.addEventListener("DOMContentLoaded", () => { const form = document.querySelector(".form-wizard"); const progress = form.querySelector(".progress"); const stepsContainer = form.querySelector(".steps-container"); const steps = form.querySelectorAll(".step"); const stepIndicators = form.querySelectorAll(".progress-container li"); const prevButton = form.querySelector(".prev-btn"); const nextButton = form.querySelector(".next-btn"); const submitButton = form.querySelector(".submit-btn"); document.documentElement.style.setProperty("--steps", stepIndicators.length); let currentStep = 0; const updateProgress = () => { let width = currentStep / (steps.length - 1); progress.style.transform = `scaleX(${width})`; stepsContainer.style.height = steps[currentStep].offsetHeight + "px"; stepIndicators.forEach((indicator, index) => { indicator.classList.toggle("current", currentStep === index); indicator.classList.toggle("done", currentStep > index); }); steps.forEach((step, index) => { step.style.transform = `translateX(-${currentStep * 100}%)`; step.classList.toggle("current", currentStep === index); }); updateButtons(); }; const updateButtons = () => { prevButton.hidden = currentStep === 0; nextButton.hidden = currentStep >= steps.length - 1; submitButton.hidden = !nextButton.hidden; }; const isValidStep = () => { const fields = steps[currentStep].querySelectorAll("input, textarea"); return [...fields].every((field) => field.reportValidity()); }; //* event listeners const inputs = form.querySelectorAll("input, textarea"); inputs.forEach((input) => input.addEventListener("focus", (e) => { const focusedElement = e.target; // get the step where the focused element belongs const focusedStep = [...steps].findIndex((step) => step.contains(focusedElement) ); if (focusedStep !== -1 && focusedStep !== currentStep) { if (!isValidStep()) return; currentStep = focusedStep; updateProgress(); } stepsContainer.scrollTop = 0; stepsContainer.scrollLeft = 0; }) ); form.addEventListener("submit", (e) => { e.preventDefault(); // prevent form submission if (!form.checkValidity()) return; const formData = new FormData(form); // send the data somewhere console.log(Object.fromEntries(formData)); submitButton.disabled = true; submitButton.textContent = "Submitting..."; // mimic a server request setTimeout(() => { form.querySelector(".completed").hidden = false; }, 3000); }); prevButton.addEventListener("click", (e) => { e.preventDefault(); // prevent form submission if (currentStep > 0) { currentStep--; updateProgress(); } }); nextButton.addEventListener("click", (e) => { e.preventDefault(); // prevent form submission if (!isValidStep()) return; if (currentStep < steps.length - 1) { currentStep++; updateProgress(); } }); updateProgress(); });
Explanation
- HTML:
- The form is divided into multiple
div
elements, each representing a step. - Each step contains form fields and navigation buttons (
Next
andPrevious
). - Only the first step is initially visible (class
active
).
- The form is divided into multiple
- CSS:
- Basic styling for the form and buttons.
- Only the
div
with the classactive
is displayed, others are hidden (display: none
).
- JavaScript:
- Event listeners for the
Next
andPrevious
buttons change the active step. - The
changeStep
function hides the current step and shows the next or previous step based on the button clicked.
- Event listeners for the
With this structure, you can easily add more steps or customize the form as needed.
Reviews
There are no reviews yet. Be the first one to write one.