-
CSS Login bag form animation
HTML
CSS
JavaScript
✕
🛍️
Click to Login
Welcome Back
Sign In
body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f4f8; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; overflow: hidden; } /* Initial Bag State */ .bag-container { position: relative; width: 140px; height: 160px; background-color: #ff6b6b; border-radius: 12px; box-shadow: 0 10px 25px rgba(255, 107, 107, 0.4); cursor: pointer; transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 30px; box-sizing: border-box; } /* Bag Handles */ .bag-container::before { content: ''; position: absolute; top: -30px; left: 50%; transform: translateX(-50%); width: 60px; height: 40px; border: 6px solid #ff6b6b; border-bottom: none; border-radius: 30px 30px 0 0; transition: all 0.5s ease; } .bag-logo { font-size: 18px; color: white; font-weight: bold; text-align: center; transition: opacity 0.3s ease; pointer-events: none; } /* Open State (Form View) */ .bag-container.open { width: 340px; height: 440px; background-color: #ffffff; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1); cursor: default; } .bag-container.open::before { opacity: 0; top: 0; transform: translateX(-50%) scale(0.5); } .bag-container.open .bag-logo { position: absolute; opacity: 0; visibility: hidden; } /* Login Form Elements */ .login-form { display: none; opacity: 0; width: 100%; flex-direction: column; align-items: center; } /* Reveal form slightly after the bag expands */ .bag-container.open .login-form { display: flex; animation: fadeIn 0.5s ease 0.3s forwards; } @keyframes fadeIn { to { opacity: 1; } } .login-form h2 { margin-top: 0; color: #333; margin-bottom: 20px; font-size: 24px; } /* Message Box Styling */ .message-box { width: 100%; padding: 10px; border-radius: 8px; margin-bottom: 16px; text-align: center; font-size: 14px; font-weight: 600; box-sizing: border-box; display: none; /* Hidden by default */ } .message-box.success { display: block; background-color: #e3f8e5; color: #27ae60; border: 1px solid #2ecc71; animation: fadeIn 0.3s ease; } .message-box.error { display: block; background-color: #ffe6e6; color: #d63031; border: 1px solid #ff7675; animation: fadeIn 0.3s ease; } .login-form input { width: 100%; padding: 14px; margin-bottom: 16px; border: 1px solid #e1e5ee; border-radius: 8px; box-sizing: border-box; outline: none; font-size: 15px; transition: border-color 0.3s, box-shadow 0.3s; } .login-form input:focus { border-color: #ff6b6b; box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.1); } .login-form button { width: 100%; padding: 14px; background-color: #ff6b6b; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: background-color 0.3s, transform 0.1s, opacity 0.3s; margin-top: 5px; } .login-form button:hover:not(:disabled) { background-color: #ff4757; } .login-form button:active:not(:disabled) { transform: scale(0.98); } .login-form button:disabled { opacity: 0.7; cursor: not-allowed; } /* Close Button */ .close-btn { position: absolute; top: 15px; right: 15px; width: 30px; height: 30px; background: #f0f4f8; border-radius: 50%; display: flex; justify-content: center; align-items: center; color: #666; font-weight: bold; cursor: pointer; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, background 0.2s; z-index: 10; } .bag-container.open .close-btn { opacity: 1; visibility: visible; transition-delay: 0.4s; } .close-btn:hover { background: #e1e5ee; color: #333; } /* Shake animation for errors */ @keyframes shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } } .shake { animation: shake 0.4s ease-in-out; }
const bagContainer = document.getElementById('bagContainer'); const closeBtn = document.getElementById('closeBtn'); const loginForm = document.getElementById('loginForm'); const messageBox = document.getElementById('messageBox'); const submitBtn = document.getElementById('submitBtn'); const emailInput = document.getElementById('emailInput'); const passwordInput = document.getElementById('passwordInput'); // Helper to reset the form state entirely function resetFormState() { loginForm.reset(); messageBox.className = 'message-box'; messageBox.textContent = ''; loginForm.classList.remove('shake'); } // Open bag when clicked (if it isn't already open) bagContainer.addEventListener('click', function() { if (!bagContainer.classList.contains('open')) { bagContainer.classList.add('open'); } }); // Close the bag and return to initial state closeBtn.addEventListener('click', function(e) { e.stopPropagation(); // Prevents bag opening immediately again bagContainer.classList.remove('open'); setTimeout(resetFormState, 400); // Wait for closing animation before resetting text }); // Prevent clicking inside the form from bubbling up loginForm.addEventListener('click', function(e) { e.stopPropagation(); }); // Handle Form Submission loginForm.addEventListener('submit', function(e) { e.preventDefault(); const email = emailInput.value; const password = passwordInput.value; // Reset previous messages and animations messageBox.className = 'message-box'; loginForm.classList.remove('shake'); // Simulate loading state submitBtn.textContent = 'Authenticating...'; submitBtn.disabled = true; // Simulate network request delay (1 second) setTimeout(() => { submitBtn.disabled = false; submitBtn.textContent = 'Sign In'; // SUCCESS CONDITION: test@test.com / password if (email === 'test@test.com' && password === 'password') { messageBox.textContent = '🎉 Login Successful!'; messageBox.classList.add('success'); // Close the bag automatically after 2 seconds setTimeout(() => { bagContainer.classList.remove('open'); setTimeout(resetFormState, 400); }, 2000); } // ERROR CONDITION else { messageBox.textContent = '❌ Invalid email or password.'; messageBox.classList.add('error'); // Trigger CSS reflow to allow the shake animation to replay if they fail twice void loginForm.offsetWidth; loginForm.classList.add('shake'); } }, 1000); });
Live Preview