1. Project Overview & Features
The Interactive Animated Login Form is an essential frontend project designed to help you bridge the gap between learning JavaScript syntax and building interactive, responsive web applications.
Key Features Implemented:
- Interactive animated mascot face that moves pupils toward input text
- Mascot covers eyes with animated hands when focusing password field
- Eye peek animation when clicking Show/Hide Password toggle
- Smooth floating input labels with glowing focus ring
- Invalid form shake effect and success celebration alert
2. Step-by-Step Code Walkthrough
A Step 1: Semantic HTML Structure
We structure the interface using clean, semantic HTML elements. This ensures maximum accessibility and provides intuitive hooks for CSS classes and JavaScript query selectors.
<div class="login-wrapper">
<!-- Interactive Animated Avatar -->
<div class="avatar-box" id="avatar">
<div class="avatar-face">
<!-- Ears -->
<div class="ear ear-left"></div>
<div class="ear ear-right"></div>
<!-- Eyes & Pupils -->
<div class="eye eye-left">
<div class="pupil" id="pupil-left"></div>
<div class="eyelid" id="eyelid-left"></div>
</div>
<div class="eye eye-right">
<div class="pupil" id="pupil-right"></div>
<div class="eyelid" id="eyelid-right"></div>
</div>
<!-- Nose & Mouth -->
<div class="nose"></div>
<div class="mouth" id="mouth"></div>
<!-- Animated Hands -->
<div class="hand hand-left" id="hand-left"></div>
<div class="hand hand-right" id="hand-right"></div>
</div>
</div>
<!-- Form Card -->
<form id="animated-login-form" class="login-form-card" novalidate>
<h2>Welcome Back!</h2>
<p class="form-sub">Please enter your credentials to login</p>
<!-- Email Field -->
<div class="input-group">
<i class="fas fa-envelope input-icon"></i>
<input type="email" id="user-email" placeholder=" " required autocomplete="off">
<label for="user-email">Email Address</label>
</div>
<!-- Password Field -->
<div class="input-group">
<i class="fas fa-lock input-icon"></i>
<input type="password" id="user-password" placeholder=" " required>
<label for="user-password">Password</label>
<button type="button" id="toggle-pwd-btn" class="toggle-pwd" title="Show/Hide Password">
<i class="far fa-eye" id="pwd-icon"></i>
</button>
</div>
<!-- Options Row -->
<div class="options-row">
<label class="remember-label">
<input type="checkbox" id="remember-me">
<span>Remember me</span>
</label>
<a href="javascript:void(0)" class="forgot-link">Forgot Password?</a>
</div>
<!-- Submit Button -->
<button type="submit" id="btn-login-submit" class="btn-login">
<span>Sign In</span>
<i class="fas fa-arrow-right"></i>
</button>
<!-- Feedback Message -->
<div id="login-feedback" class="login-feedback hidden"></div>
</form>
</div>B Step 2: Styling & Responsive CSS
Modern CSS flexbox and grid layouts are used to make the UI look polished, centered, and responsive across desktop, tablet, and mobile screens.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%);
color: #f8fafc;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-wrapper {
width: 100%;
max-width: 400px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
/* Avatar Styling */
.avatar-box {
width: 130px;
height: 110px;
position: relative;
margin-bottom: -30px;
z-index: 5;
}
.avatar-face {
width: 100%;
height: 100%;
background: #fbcfe8;
border: 4px solid #334155;
border-radius: 65px 65px 50px 50px;
position: relative;
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
overflow: hidden;
}
.ear {
width: 28px;
height: 28px;
background: #f472b6;
border: 3px solid #334155;
border-radius: 50%;
position: absolute;
top: 10px;
}
.ear-left { left: 8px; }
.ear-right { right: 8px; }
/* Eyes & Pupils */
.eye {
width: 32px;
height: 32px;
background: #ffffff;
border: 3px solid #334155;
border-radius: 50%;
position: absolute;
top: 36px;
overflow: hidden;
}
.eye-left { left: 24px; }
.eye-right { right: 24px; }
.pupil {
width: 14px;
height: 14px;
background: #0f172a;
border-radius: 50%;
position: absolute;
top: 7px;
left: 7px;
transition: transform 0.1s ease;
}
.eyelid {
position: absolute;
inset: 0;
background: #fbcfe8;
border-bottom: 2px solid #334155;
transform: translateY(-100%);
transition: transform 0.2s ease;
}
.eye.blink .eyelid {
transform: translateY(0);
}
.nose {
width: 10px;
height: 8px;
background: #f472b6;
border-radius: 5px;
position: absolute;
top: 54px;
left: 50%;
transform: translateX(-50%);
}
.mouth {
width: 18px;
height: 8px;
border-bottom: 3px solid #334155;
border-radius: 0 0 12px 12px;
position: absolute;
top: 68px;
left: 50%;
transform: translateX(-50%);
transition: all 0.2s ease;
}
.mouth.smile {
height: 12px;
border-radius: 0 0 16px 16px;
background: #ef4444;
}
/* Hands for covering eyes */
.hand {
width: 38px;
height: 38px;
background: #f472b6;
border: 3px solid #334155;
border-radius: 50%;
position: absolute;
bottom: -40px;
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
z-index: 10;
}
.hand-left { left: 16px; }
.hand-right { right: 16px; }
.avatar-box.covering-eyes .hand-left {
transform: translateY(-56px) rotate(15deg);
}
.avatar-box.covering-eyes .hand-right {
transform: translateY(-56px) rotate(-15deg);
}
.avatar-box.peeking .hand-left {
transform: translateY(-44px) rotate(30deg);
}
.avatar-box.peeking .hand-right {
transform: translateY(-56px) rotate(-15deg);
}
/* Form Card */
.login-form-card {
background: #1e293b;
border: 1px solid #334155;
border-radius: 24px;
padding: 44px 28px 28px;
width: 100%;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
position: relative;
z-index: 2;
transition: transform 0.2s ease;
}
.login-form-card.shake {
animation: shakeForm 0.4s ease-in-out;
}
@keyframes shakeForm {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-8px); }
40%, 80% { transform: translateX(8px); }
}
.login-form-card h2 {
font-size: 1.5rem;
font-weight: 700;
color: #f8fafc;
text-align: center;
margin-bottom: 4px;
}
.form-sub {
color: #94a3b8;
font-size: 0.85rem;
text-align: center;
margin-bottom: 24px;
}
/* Floating Input Groups */
.input-group {
position: relative;
margin-bottom: 18px;
}
.input-icon {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
color: #64748b;
font-size: 1rem;
transition: color 0.2s;
pointer-events: none;
}
.input-group input {
width: 100%;
background: #0f172a;
border: 1.5px solid #334155;
border-radius: 12px;
padding: 14px 42px 14px 42px;
color: #f8fafc;
font-size: 0.95rem;
outline: none;
transition: all 0.25s ease;
}
.input-group input:focus {
border-color: #6366f1;
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.15);
}
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
top: -8px;
left: 14px;
font-size: 0.72rem;
color: #818cf8;
background: #1e293b;
padding: 0 6px;
}
.input-group label {
position: absolute;
left: 42px;
top: 50%;
transform: translateY(-50%);
color: #64748b;
font-size: 0.9rem;
pointer-events: none;
transition: all 0.2s ease;
}
.toggle-pwd {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
color: #94a3b8;
cursor: pointer;
padding: 4px 6px;
}
.toggle-pwd:hover { color: #f8fafc; }
.options-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.82rem;
color: #94a3b8;
margin-bottom: 22px;
}
.remember-label {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
}
.remember-label input { accent-color: #6366f1; }
.forgot-link { color: #818cf8; text-decoration: none; }
.forgot-link:hover { text-decoration: underline; }
.btn-login {
width: 100%;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: #ffffff;
border: none;
padding: 14px;
border-radius: 12px;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.15s, box-shadow 0.2s;
box-shadow: 0 10px 20px -5px rgba(99, 102, 241, 0.4);
}
.btn-login:hover {
transform: translateY(-2px);
box-shadow: 0 15px 25px -5px rgba(99, 102, 241, 0.5);
}
.btn-login:active { transform: translateY(0); }
.login-feedback {
margin-top: 14px;
padding: 10px 14px;
border-radius: 10px;
font-size: 0.85rem;
text-align: center;
font-weight: 600;
}
.login-feedback.error {
background: rgba(239, 68, 68, 0.15);
color: #f87171;
border: 1px solid rgba(239, 68, 68, 0.3);
}
.login-feedback.success {
background: rgba(16, 185, 129, 0.15);
color: #34d399;
border: 1px solid rgba(16, 185, 129, 0.3);
}
.hidden { display: none; }C Step 3: JavaScript Logic & Event Handling
Here is the complete JavaScript code handling the state, event listeners, mathematical logic, and dynamic DOM rendering:
const avatar = document.getElementById('avatar');
const pupilLeft = document.getElementById('pupil-left');
const pupilRight = document.getElementById('pupil-right');
const emailInput = document.getElementById('user-email');
const passwordInput = document.getElementById('user-password');
const togglePwdBtn = document.getElementById('toggle-pwd-btn');
const pwdIcon = document.getElementById('pwd-icon');
const mouth = document.getElementById('mouth');
const form = document.getElementById('animated-login-form');
const feedback = document.getElementById('login-feedback');
const loginBtn = document.getElementById('btn-login-submit');
// Track typing length in Email to rotate pupils
emailInput.addEventListener('input', (e) => {
const length = e.target.value.length;
// Map string length to pupil X translation (-4px to +6px)
const xOffset = Math.min(6, Math.max(-4, (length - 10) * 0.6));
const yOffset = 3; // look down towards field
pupilLeft.style.transform = `translate(${xOffset}px, ${yOffset}px)`;
pupilRight.style.transform = `translate(${xOffset}px, ${yOffset}px)`;
mouth.classList.toggle('smile', length > 5);
});
emailInput.addEventListener('focus', () => {
mouth.classList.add('smile');
pupilLeft.style.transform = `translate(0px, 3px)`;
pupilRight.style.transform = `translate(0px, 3px)`;
});
emailInput.addEventListener('blur', () => {
pupilLeft.style.transform = `translate(0px, 0px)`;
pupilRight.style.transform = `translate(0px, 0px)`;
mouth.classList.remove('smile');
});
// Password Focus: Cover eyes
passwordInput.addEventListener('focus', () => {
avatar.classList.add('covering-eyes');
avatar.classList.remove('peeking');
});
passwordInput.addEventListener('blur', () => {
avatar.classList.remove('covering-eyes');
avatar.classList.remove('peeking');
});
// Toggle Show/Hide Password
togglePwdBtn.addEventListener('click', () => {
const isPassword = passwordInput.type === 'password';
passwordInput.type = isPassword ? 'text' : 'password';
pwdIcon.className = isPassword ? 'far fa-eye-slash' : 'far fa-eye';
if (isPassword) {
avatar.classList.remove('covering-eyes');
avatar.classList.add('peeking');
} else {
avatar.classList.add('covering-eyes');
avatar.classList.remove('peeking');
}
});
// Form Submission with Validation & Shake
form.addEventListener('submit', (e) => {
e.preventDefault();
const email = emailInput.value.trim();
const password = passwordInput.value.trim();
feedback.classList.remove('hidden', 'error', 'success');
if (!email || !email.includes('@') || password.length < 6) {
form.classList.add('shake');
setTimeout(() => form.classList.remove('shake'), 450);
feedback.classList.add('error');
feedback.innerHTML = '<i class="fas fa-exclamation-circle me-1"></i> Please enter a valid email & at least 6 characters.';
return;
}
// Success state
feedback.classList.add('success');
feedback.innerHTML = '<i class="fas fa-check-circle me-1"></i> Login successful! Redirecting...';
loginBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Authenticating...';
setTimeout(() => {
loginBtn.innerHTML = '<i class="fas fa-check"></i> Welcome!';
loginBtn.style.background = '#10b981';
}, 1200);
});3. Core JavaScript Concepts You Learned
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.
4. How to Run Locally on Your Computer
- Download the project ZIP: Click the Download Project (.ZIP) button at the top or in the live runner to save
animated-login-form-project.zip. - Extract the archive: Unzip the downloaded file. Inside you will find separate
index.html,style.css,script.js,index-standalone.html, and a comprehensiveREADME.md. - Open in any browser: Simply double-click
index.html(orindex-standalone.html) to open and test immediately in Chrome, Firefox, Safari, or Edge. - Edit in Visual Studio Code: Open the extracted folder in VS Code. Right-click
index.htmland select "Open with Live Server" to enjoy instant live reloading as you modify HTML, CSS, or JS!
Bonus Challenges for Learners
Ready to level up? Try adding these extra features on your own:
- Add sound effects or audio feedback for user button clicks.
- Add custom theme color customizer with CSS variables.
- Export the results to PDF or copy as a formatted text summary.