-
3D Box Loader Animation
HTML
CSS
JavaScript
Verification Code
Enter the 6-digit code (Demo:
123456
)
01:00
Resend Code
Verify Account
* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Poppins', sans-serif; height: 100vh; display: grid; place-items: center; background: #09090e; overflow: hidden; position: relative; } /* Ambient Animated Background */ .ambient-light { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.6; animation: float 10s infinite alternate ease-in-out; } .light-1 { width: 300px; height: 300px; background: #3b82f6; top: -10%; left: -10%; } .light-2 { width: 400px; height: 400px; background: #8b5cf6; bottom: -20%; right: -10%; animation-delay: -5s; } .light-3 { width: 200px; height: 200px; background: #06b6d4; bottom: 20%; left: 20%; animation-duration: 15s; } @keyframes float { 0% { transform: translate(0, 0) scale(1); } 100% { transform: translate(30px, 50px) scale(1.2); } } /* Card Styles */ .card { position: relative; width: min(92vw, 420px); padding: 40px 32px; border-radius: 28px; background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid rgba(255, 255, 255, 0.08); color: #fff; box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 10; } .card.ok { box-shadow: 0 0 40px rgba(34, 197, 94, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1); border-color: rgba(34, 197, 94, 0.5); } .card.err { animation: shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; border-color: rgba(220, 38, 38, 0.5); } @keyframes shake { 10%, 90% { transform: translateX(-2px); } 20%, 80% { transform: translateX(4px); } 30%, 50%, 70% { transform: translateX(-8px); } 40%, 60% { transform: translateX(8px); } } /* Header & Text */ .icon-container { width: 64px; height: 64px; margin: 0 auto 20px; background: rgba(255, 255, 255, 0.05); border-radius: 50%; display: grid; place-items: center; border: 1px solid rgba(255, 255, 255, 0.1); } .icon-container svg { width: 32px; height: 32px; fill: none; stroke: #a78bfa; stroke-width: 2; } h2 { text-align: center; font-weight: 600; font-size: 24px; margin-bottom: 8px; letter-spacing: 0.5px; } p { text-align: center; color: #94a3b8; font-size: 14px; margin-bottom: 24px; font-weight: 300; } p span { color: #fff; font-weight: 500; } /* OTP Inputs */ .otp { display: flex; gap: 12px; justify-content: center; margin-bottom: 24px; } .otp input { width: 50px; height: 60px; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 14px; text-align: center; font-size: 24px; font-weight: 600; font-family: 'Poppins', sans-serif; background: rgba(0, 0, 0, 0.2); color: #fff; transition: all 0.3s ease; box-shadow: inset 0 2px 4px rgba(0,0,0,0.2); } .otp input:focus { outline: none; border-color: #8b5cf6; background: rgba(139, 92, 246, 0.1); box-shadow: 0 0 15px rgba(139, 92, 246, 0.3), inset 0 2px 4px rgba(0,0,0,0.1); transform: translateY(-2px); } /* Disable arrows in number inputs for some browsers */ .otp input::-webkit-outer-spin-button, .otp input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } /* Progress Bar */ .bar { height: 6px; background: rgba(255, 255, 255, 0.1); border-radius: 20px; overflow: hidden; margin-bottom: 16px; } .fill { height: 100%; width: 0; background: linear-gradient(90deg, #3b82f6, #8b5cf6); transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1); } /* Meta details */ .meta { display: flex; justify-content: space-between; margin-bottom: 24px; font-size: 13px; font-weight: 500; } .timer-wrapper { color: #94a3b8; display: flex; align-items: center; gap: 6px; } .timer-wrapper svg { width: 16px; height: 16px; stroke: #94a3b8; fill: none; stroke-width: 2; } #resend { color: #8b5cf6; text-decoration: none; pointer-events: none; opacity: 0.5; transition: 0.3s; } #resend:hover { text-decoration: underline; text-underline-offset: 4px; } /* Button */ button { width: 100%; padding: 16px; border: none; border-radius: 14px; background: linear-gradient(135deg, #6d28d9, #4f46e5); color: #fff; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 8px 20px rgba(79, 70, 229, 0.3); font-family: 'Poppins', sans-serif; } button:hover { transform: translateY(-2px); box-shadow: 0 10px 25px rgba(79, 70, 229, 0.4); } button:active { transform: translateY(1px); box-shadow: 0 4px 10px rgba(79, 70, 229, 0.3); } /* Message */ .msg { margin-top: 16px; padding: 12px; border-radius: 10px; text-align: center; font-size: 14px; font-weight: 500; opacity: 0; visibility: hidden; transform: translateY(10px); transition: all 0.4s ease; } .msg.show { opacity: 1; visibility: visible; transform: translateY(0); } .success { background: rgba(34, 197, 94, 0.1); color: #4ade80; border: 1px solid rgba(34, 197, 94, 0.2); } .error { background: rgba(220, 38, 38, 0.1); color: #f87171; border: 1px solid rgba(220, 38, 38, 0.2); }
const inputs = [...document.querySelectorAll(".otp input")]; const fill = document.getElementById("fill"); const otpContainer = document.getElementById("otpContainer"); // Input Logic (Typing and Auto-advance) inputs.forEach((input, index) => { input.addEventListener("input", () => { // Allow only numbers input.value = input.value.replace(/[^0-9]/g, ''); if (input.value && index < 5) { inputs[index + 1].focus(); } updateProgressBar(); }); // Backspace Logic input.addEventListener("keydown", (e) => { if (e.key === "Backspace" && !input.value && index > 0) { inputs[index - 1].focus(); } }); }); // Advanced: Handle Paste Event across inputs otpContainer.addEventListener("paste", (e) => { e.preventDefault(); const pastedData = e.clipboardData.getData("text").replace(/[^0-9]/g, '').slice(0, 6); if (!pastedData) return; pastedData.split("").forEach((char, index) => { if (index < inputs.length) { inputs[index].value = char; } }); // Focus next empty input or last input const nextEmptyIndex = inputs.findIndex(input => input.value === ""); if (nextEmptyIndex !== -1) { inputs[nextEmptyIndex].focus(); } else { inputs[5].focus(); } updateProgressBar(); }); function updateProgressBar() { const filledCount = inputs.filter(input => input.value).length; fill.style.width = (filledCount / 6 * 100) + '%'; } // Timer Logic let timeLeft = 60; const timerElement = document.getElementById('timer'); const resendLink = document.getElementById('resend'); let timerInterval; function startTimer() { clearInterval(timerInterval); timeLeft = 60; resendLink.style.pointerEvents = 'none'; resendLink.style.opacity = '0.5'; timerInterval = setInterval(() => { if (timeLeft > 0) { timeLeft--; timerElement.textContent = '00:' + String(timeLeft).padStart(2, '0'); } else { clearInterval(timerInterval); resendLink.style.pointerEvents = 'auto'; resendLink.style.opacity = '1'; } }, 1000); } startTimer(); resendLink.addEventListener('click', (e) => { e.preventDefault(); startTimer(); // Optional: reset inputs on resend inputs.forEach(input => input.value = ''); updateProgressBar(); inputs[0].focus(); }); // Verification Logic const verifyBtn = document.getElementById('verify'); const msg = document.getElementById('msg'); const card = document.getElementById('card'); const iconBox = document.getElementById('iconBox'); verifyBtn.addEventListener('click', () => { const otp = inputs.map(input => input.value).join(''); msg.className = 'msg'; // reset classes card.classList.remove('ok', 'err'); if (otp.length < 6) { showError('Please enter all 6 digits.'); return; } if (otp === '123456') { card.classList.add('ok'); msg.textContent = 'Authentication Successful!'; msg.classList.add('show', 'success'); // Change icon to a checkmark on success iconBox.innerHTML = '
'; iconBox.style.borderColor = 'rgba(34, 197, 94, 0.4)'; iconBox.style.background = 'rgba(34, 197, 94, 0.1)'; } else { showError('Invalid Code. Please try again.'); // Clear inputs on error (Optional but good UX) inputs.forEach(input => input.value = ''); updateProgressBar(); inputs[0].focus(); } }); function showError(text) { card.classList.add('err'); msg.textContent = text; msg.classList.add('show', 'error'); // Remove error animation class after it completes so it can trigger again setTimeout(() => card.classList.remove('err'), 500); }
Live Preview