Glassmorphism Login Form with Social Auth
A sleek glassmorphism login form with animated background blobs, two-panel layout, live inline validation, password toggle, remember me checkbox, Google & GitHub social login buttons, and a quote + stats right panel — built with pure HTML, CSS & Vanilla JavaScript.
- Published 22 Aug 2026
- 116 views
Overview
Glassmorphism Login Form with Social Auth
A sleek two-panel glassmorphism login form with animated floating background blobs, live inline validation, password visibility toggle, remember me checkbox, Google & GitHub social login buttons, and an inspirational quote + stats grid on the right panel — built with pure HTML, CSS & Vanilla JavaScript. No frameworks, no build tools needed.
What's Included
- Two-panel layout — login form left panel + quote & stats right panel
- Glassmorphism card with
backdrop-filter: blurand glass border - Three animated floating background blobs (indigo, pink, cyan)
fadeSlideUpentrance animation on page load- Live inline email validation on blur and input
- Live password validation (min 6 characters) on input
- Error state (red border + message) and success state (green border) per field
- Password visibility toggle
- Remember me custom styled checkbox
- "Forgot password?" link in label row
- Google and GitHub social login buttons with hover ripple effect
- Right panel — quote block with author avatar, 2x2 stats grid (Users, Rating, Countries, Uptime)
- Submit button with success state (
Welcome Back!) - Responsive — stacks to single column on mobile below
700px - All design tokens via CSS custom properties
- Zero dependencies — pure HTML, CSS, Vanilla JS
Dependencies
| Name | Version | Type |
|---|---|---|
| Google Fonts — Syne + DM Sans | latest | CDN |
| Material Icons Round | latest | CDN |
Copy all CDN links from the
<head>ofindex.htmland paste into your project before your stylesheet.
Desktop Preview

HTML
Copy the full markup — background blob divs, wrapper, form panel, and right panel — and paste inside your <body>.
html1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>Login | Nexora</title> 7 8 <!-- Google Fonts --> 9 <link rel="preconnect" href="https://fonts.googleapis.com" /> 10 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 11 <link 12 href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:wght@300;400;500&display=swap" 13 rel="stylesheet" 14 /> 15 16 <!-- Google Material Icons --> 17 <link 18 href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" 19 rel="stylesheet" 20 /> 21 22 <link rel="stylesheet" href="style.css" /> 23 </head> 24 <body> 25 <!-- Animated Background Blobs --> 26 <div class="bg-blob blob1"></div> 27 <div class="bg-blob blob2"></div> 28 <div class="bg-blob blob3"></div> 29 30 <div class="wrapper"> 31 <!-- Login Form Panel --> 32 <div class="form-panel"> 33 <div class="brand"> 34 <span class="material-icons-round brand-icon">electric_bolt</span> 35 <span class="brand-name">Nexora</span> 36 </div> 37 38 <div class="form-header"> 39 <h2>Welcome Back</h2> 40 <p class="form-sub">Sign in to continue your journey.</p> 41 </div> 42 43 <form id="loginForm" novalidate> 44 <!-- Email --> 45 <div class="form-group"> 46 <label for="email">Email Address</label> 47 <div class="input-box"> 48 <span class="material-icons-round">mail</span> 49 <input type="email" id="email" placeholder="arjun@example.com" /> 50 </div> 51 <span class="error-msg" id="emailErr"></span> 52 </div> 53 54 <!-- Password --> 55 <div class="form-group"> 56 <div class="label-row"> 57 <label for="password">Password</label> 58 <a href="#" class="forgot-link">Forgot password?</a> 59 </div> 60 <div class="input-box"> 61 <span class="material-icons-round">lock</span> 62 <input 63 type="password" 64 id="password" 65 placeholder="Enter your password" 66 /> 67 <span class="material-icons-round toggle-pass" id="togglePass" 68 >visibility_off</span 69 > 70 </div> 71 <span class="error-msg" id="passwordErr"></span> 72 </div> 73 74 <!-- Remember Me --> 75 <div class="form-group remember-group"> 76 <label class="checkbox-label"> 77 <input type="checkbox" id="remember" /> 78 <span class="custom-check"></span> 79 Remember me for 30 days 80 </label> 81 </div> 82 83 <!-- Submit --> 84 <button type="submit" class="submit-btn"> 85 <span>Sign In</span> 86 <span class="material-icons-round">arrow_forward</span> 87 </button> 88 89 <!-- Divider --> 90 <div class="divider"> 91 <span></span> 92 <p>or continue with</p> 93 <span></span> 94 </div> 95 96 <!-- Social Buttons --> 97 <div class="social-row"> 98 <button type="button" class="social-btn"> 99 <img 100 src="https://www.svgrepo.com/show/475656/google-color.svg" 101 alt="Google" 102 width="18" 103 height="18" 104 /> 105 Google 106 </button> 107 <button type="button" class="social-btn"> 108 <img 109 src="https://www.svgrepo.com/show/448234/github.svg" 110 alt="GitHub" 111 width="18" 112 height="18" 113 style="filter: invert(1)" 114 /> 115 GitHub 116 </button> 117 </div> 118 </form> 119 120 <p class="no-account"> 121 Don't have an account? <a href="index.html">Create one</a> 122 </p> 123 </div> 124 125 <!-- Right Panel --> 126 <div class="right-panel"> 127 <div class="right-inner"> 128 <div class="quote-block"> 129 <span class="material-icons-round quote-icon">format_quote</span> 130 <p class="quote-text"> 131 The future belongs to those who believe in the beauty of their 132 dreams. 133 </p> 134 <div class="quote-author"> 135 <div class="author-avatar">ES</div> 136 <div> 137 <p class="author-name">Eleanor S.</p> 138 <p class="author-role">Product Designer</p> 139 </div> 140 </div> 141 </div> 142 143 <div class="stats-grid"> 144 <div class="stat-card"> 145 <span class="material-icons-round">group</span> 146 <p class="stat-num">128K+</p> 147 <p class="stat-label">Active Users</p> 148 </div> 149 <div class="stat-card"> 150 <span class="material-icons-round">star</span> 151 <p class="stat-num">4.9</p> 152 <p class="stat-label">Rating</p> 153 </div> 154 <div class="stat-card"> 155 <span class="material-icons-round">public</span> 156 <p class="stat-num">60+</p> 157 <p class="stat-label">Countries</p> 158 </div> 159 <div class="stat-card"> 160 <span class="material-icons-round">security</span> 161 <p class="stat-num">99.9%</p> 162 <p class="stat-label">Uptime</p> 163 </div> 164 </div> 165 </div> 166 </div> 167 </div> 168 169 <script src="script.js"></script> 170 </body> 171</html>
CSS
Paste into your stylesheet. Change --accent in :root to your brand color and inputs, buttons, and highlights update automatically.
css1/* =========================== 2 CSS VARIABLES & RESET 3=========================== */ 4*, 5*::before, 6*::after { 7 box-sizing: border-box; 8 margin: 0; 9 padding: 0; 10} 11 12:root { 13 --glass-bg: rgba(255, 255, 255, 0.08); 14 --glass-border: rgba(255, 255, 255, 0.18); 15 --glass-shadow: rgba(0, 0, 0, 0.35); 16 --accent: #a78bfa; 17 --accent2: #60a5fa; 18 --accent3: #f472b6; 19 --text-main: #f1f5f9; 20 --text-sub: #94a3b8; 21 --input-bg: rgba(255, 255, 255, 0.07); 22 --input-border: rgba(255, 255, 255, 0.12); 23 --input-focus: rgba(167, 139, 250, 0.5); 24 --error: #f87171; 25 --success: #34d399; 26 --font-display: "Syne", sans-serif; 27 --font-body: "DM Sans", sans-serif; 28} 29 30html, 31body { 32 height: 100%; 33 width: 100%; 34} 35 36body { 37 min-height: 100vh; 38 display: flex; 39 align-items: center; 40 justify-content: center; 41 font-family: var(--font-body); 42 background: #0a0a1a; 43 color: var(--text-main); 44 overflow-x: hidden; 45 position: relative; 46} 47 48/* =========================== 49 ANIMATED BACKGROUND BLOBS 50=========================== */ 51.bg-blob { 52 position: fixed; 53 border-radius: 50%; 54 filter: blur(90px); 55 opacity: 0.45; 56 animation: floatBlob 12s ease-in-out infinite alternate; 57 z-index: 0; 58 pointer-events: none; 59} 60 61.blob1 { 62 width: 500px; 63 height: 500px; 64 background: radial-gradient(circle, #4f46e5, #7c3aed); 65 top: -120px; 66 right: -100px; 67 animation-delay: 0s; 68} 69 70.blob2 { 71 width: 420px; 72 height: 420px; 73 background: radial-gradient(circle, #f43f5e, #ec4899); 74 bottom: -100px; 75 left: -80px; 76 animation-delay: 3s; 77} 78 79.blob3 { 80 width: 350px; 81 height: 350px; 82 background: radial-gradient(circle, #06b6d4, #0ea5e9); 83 top: 40%; 84 right: 30%; 85 animation-delay: 6s; 86} 87 88@keyframes floatBlob { 89 0% { 90 transform: scale(1) translate(0, 0); 91 } 92 50% { 93 transform: scale(1.1) translate(-20px, 30px); 94 } 95 100% { 96 transform: scale(0.95) translate(20px, -20px); 97 } 98} 99 100/* =========================== 101 MAIN WRAPPER 102=========================== */ 103.wrapper { 104 position: relative; 105 z-index: 1; 106 display: flex; 107 width: min(920px, 95vw); 108 min-height: 560px; 109 border-radius: 28px; 110 overflow: hidden; 111 background: var(--glass-bg); 112 backdrop-filter: blur(28px) saturate(160%); 113 -webkit-backdrop-filter: blur(28px) saturate(160%); 114 border: 1px solid var(--glass-border); 115 box-shadow: 116 0 32px 80px var(--glass-shadow), 117 inset 0 1px 0 rgba(255, 255, 255, 0.12); 118 animation: fadeSlideUp 0.7s cubic-bezier(0.22, 1, 0.36, 1) both; 119} 120 121@keyframes fadeSlideUp { 122 from { 123 opacity: 0; 124 transform: translateY(40px) scale(0.97); 125 } 126 to { 127 opacity: 1; 128 transform: translateY(0) scale(1); 129 } 130} 131 132/* =========================== 133 FORM PANEL (LEFT) 134=========================== */ 135.form-panel { 136 width: 50%; 137 padding: 48px 44px; 138 display: flex; 139 flex-direction: column; 140 justify-content: center; 141 gap: 0; 142 border-right: 1px solid var(--glass-border); 143} 144 145/* Brand */ 146.brand { 147 display: flex; 148 align-items: center; 149 gap: 10px; 150 margin-bottom: 36px; 151} 152 153.brand-icon { 154 font-size: 26px; 155 color: var(--accent); 156 background: rgba(167, 139, 250, 0.15); 157 padding: 6px; 158 border-radius: 10px; 159 border: 1px solid rgba(167, 139, 250, 0.3); 160} 161 162.brand-name { 163 font-family: var(--font-display); 164 font-size: 20px; 165 font-weight: 800; 166 letter-spacing: -0.5px; 167 background: linear-gradient(135deg, var(--accent), var(--accent2)); 168 -webkit-background-clip: text; 169 -webkit-text-fill-color: transparent; 170 background-clip: text; 171} 172 173/* Form Header */ 174.form-header { 175 margin-bottom: 28px; 176} 177 178.form-header h2 { 179 font-family: var(--font-display); 180 font-size: 30px; 181 font-weight: 800; 182 letter-spacing: -0.5px; 183 margin-bottom: 6px; 184} 185 186.form-sub { 187 font-size: 13.5px; 188 color: var(--text-sub); 189} 190 191/* =========================== 192 FORM GROUPS 193=========================== */ 194form { 195 display: flex; 196 flex-direction: column; 197 gap: 18px; 198} 199 200.form-group { 201 display: flex; 202 flex-direction: column; 203 gap: 6px; 204} 205 206label { 207 font-size: 12px; 208 font-weight: 500; 209 color: var(--text-sub); 210 letter-spacing: 0.5px; 211 text-transform: uppercase; 212} 213 214.label-row { 215 display: flex; 216 align-items: center; 217 justify-content: space-between; 218} 219 220.forgot-link { 221 font-size: 12px; 222 color: var(--accent); 223 text-decoration: none; 224 font-weight: 500; 225 transition: color 0.2s; 226} 227 228.forgot-link:hover { 229 color: var(--accent3); 230} 231 232/* =========================== 233 INPUT BOX 234=========================== */ 235.input-box { 236 display: flex; 237 align-items: center; 238 gap: 10px; 239 background: var(--input-bg); 240 border: 1px solid var(--input-border); 241 border-radius: 12px; 242 padding: 0 14px; 243 height: 50px; 244 transition: 245 border-color 0.3s, 246 box-shadow 0.3s, 247 background 0.3s; 248} 249 250.input-box:focus-within { 251 border-color: var(--input-focus); 252 background: rgba(167, 139, 250, 0.08); 253 box-shadow: 254 0 0 0 3px rgba(167, 139, 250, 0.12), 255 0 4px 20px rgba(167, 139, 250, 0.1); 256} 257 258.input-box .material-icons-round { 259 font-size: 18px; 260 color: var(--text-sub); 261 flex-shrink: 0; 262 transition: color 0.3s; 263} 264 265.input-box:focus-within .material-icons-round:first-child { 266 color: var(--accent); 267} 268 269.input-box input { 270 flex: 1; 271 background: transparent; 272 border: none; 273 outline: none; 274 font-family: var(--font-body); 275 font-size: 14.5px; 276 color: var(--text-main); 277} 278 279.input-box input::placeholder { 280 color: rgba(148, 163, 184, 0.45); 281} 282 283.toggle-pass { 284 cursor: pointer; 285 font-size: 18px !important; 286 color: var(--text-sub) !important; 287 transition: color 0.2s; 288 user-select: none; 289} 290 291.toggle-pass:hover { 292 color: var(--accent) !important; 293} 294 295/* =========================== 296 ERROR MESSAGES 297=========================== */ 298.error-msg { 299 font-size: 11.5px; 300 color: var(--error); 301 min-height: 14px; 302 padding-left: 4px; 303 display: block; 304 animation: fadeIn 0.2s ease; 305} 306 307.input-box.error-state { 308 border-color: rgba(248, 113, 113, 0.5); 309 box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.1); 310} 311 312.input-box.success-state { 313 border-color: rgba(52, 211, 153, 0.4); 314} 315 316@keyframes fadeIn { 317 from { 318 opacity: 0; 319 transform: translateY(-4px); 320 } 321 to { 322 opacity: 1; 323 transform: translateY(0); 324 } 325} 326 327/* =========================== 328 REMEMBER ME CHECKBOX 329=========================== */ 330.remember-group { 331 margin-top: -4px; 332} 333 334.checkbox-label { 335 display: flex; 336 align-items: center; 337 gap: 10px; 338 cursor: pointer; 339 font-size: 13px; 340 color: var(--text-sub); 341 text-transform: none; 342 letter-spacing: 0; 343 font-weight: 400; 344 user-select: none; 345} 346 347.checkbox-label input[type="checkbox"] { 348 display: none; 349} 350 351.custom-check { 352 width: 18px; 353 height: 18px; 354 border-radius: 5px; 355 border: 1.5px solid var(--input-border); 356 background: var(--input-bg); 357 flex-shrink: 0; 358 display: flex; 359 align-items: center; 360 justify-content: center; 361 transition: all 0.3s; 362} 363 364.checkbox-label input[type="checkbox"]:checked + .custom-check { 365 background: var(--accent); 366 border-color: var(--accent); 367 box-shadow: 0 0 10px rgba(167, 139, 250, 0.5); 368} 369 370.checkbox-label input[type="checkbox"]:checked + .custom-check::after { 371 content: ""; 372 display: block; 373 width: 5px; 374 height: 9px; 375 border: 2px solid #fff; 376 border-top: none; 377 border-left: none; 378 transform: rotate(45deg) translateY(-1px); 379} 380 381/* =========================== 382 SUBMIT BUTTON 383=========================== */ 384.submit-btn { 385 display: flex; 386 align-items: center; 387 justify-content: center; 388 gap: 8px; 389 width: 100%; 390 height: 52px; 391 border: none; 392 border-radius: 14px; 393 background: linear-gradient(135deg, #7c3aed, #4f46e5); 394 color: #fff; 395 font-family: var(--font-display); 396 font-size: 15px; 397 font-weight: 700; 398 letter-spacing: 0.3px; 399 cursor: pointer; 400 position: relative; 401 overflow: hidden; 402 transition: 403 transform 0.2s, 404 box-shadow 0.3s; 405 box-shadow: 0 8px 30px rgba(124, 58, 237, 0.4); 406 margin-top: 2px; 407} 408 409.submit-btn::before { 410 content: ""; 411 position: absolute; 412 inset: 0; 413 background: linear-gradient(135deg, #9f67fa, #6366f1); 414 opacity: 0; 415 transition: opacity 0.3s; 416} 417 418.submit-btn:hover { 419 transform: translateY(-2px); 420 box-shadow: 0 12px 40px rgba(124, 58, 237, 0.55); 421} 422 423.submit-btn:hover::before { 424 opacity: 1; 425} 426.submit-btn:active { 427 transform: translateY(0); 428} 429 430.submit-btn span, 431.submit-btn .material-icons-round { 432 position: relative; 433 z-index: 1; 434} 435 436.submit-btn .material-icons-round { 437 font-size: 20px; 438 transition: transform 0.3s; 439} 440 441.submit-btn:hover .material-icons-round { 442 transform: translateX(4px); 443} 444 445.submit-btn.success-state { 446 background: linear-gradient(135deg, #059669, #10b981); 447 box-shadow: 0 8px 30px rgba(16, 185, 129, 0.4); 448 pointer-events: none; 449} 450 451/* =========================== 452 DIVIDER 453=========================== */ 454.divider { 455 display: flex; 456 align-items: center; 457 gap: 12px; 458 margin: 2px 0; 459} 460 461.divider span { 462 flex: 1; 463 height: 1px; 464 background: var(--glass-border); 465} 466 467.divider p { 468 font-size: 12px; 469 color: var(--text-sub); 470 white-space: nowrap; 471} 472 473/* =========================== 474 SOCIAL BUTTONS 475=========================== */ 476.social-row { 477 display: grid; 478 grid-template-columns: 1fr 1fr; 479 gap: 12px; 480} 481 482.social-btn { 483 display: flex; 484 align-items: center; 485 justify-content: center; 486 gap: 8px; 487 height: 44px; 488 border-radius: 12px; 489 border: 1px solid var(--glass-border); 490 background: var(--input-bg); 491 color: var(--text-main); 492 font-family: var(--font-body); 493 font-size: 13.5px; 494 font-weight: 500; 495 cursor: pointer; 496 transition: 497 background 0.3s, 498 border-color 0.3s, 499 transform 0.2s; 500} 501 502.social-btn:hover { 503 background: rgba(255, 255, 255, 0.1); 504 border-color: rgba(255, 255, 255, 0.25); 505 transform: translateY(-1px); 506} 507 508/* =========================== 509 NO ACCOUNT LINK 510=========================== */ 511.no-account { 512 font-size: 13px; 513 color: var(--text-sub); 514 text-align: center; 515 margin-top: 22px; 516} 517 518.no-account a { 519 color: var(--accent); 520 text-decoration: none; 521 font-weight: 600; 522 transition: color 0.2s; 523} 524 525.no-account a:hover { 526 color: var(--accent3); 527} 528 529/* =========================== 530 RIGHT PANEL 531=========================== */ 532.right-panel { 533 flex: 1; 534 display: flex; 535 align-items: center; 536 justify-content: center; 537 padding: 48px 36px; 538 background: linear-gradient( 539 145deg, 540 rgba(79, 70, 229, 0.2), 541 rgba(236, 72, 153, 0.1) 542 ); 543 position: relative; 544 overflow: hidden; 545} 546 547.right-panel::before { 548 content: ""; 549 position: absolute; 550 width: 300px; 551 height: 300px; 552 background: radial-gradient( 553 circle, 554 rgba(167, 139, 250, 0.2), 555 transparent 70% 556 ); 557 bottom: -60px; 558 left: -60px; 559 border-radius: 50%; 560 pointer-events: none; 561} 562 563.right-inner { 564 display: flex; 565 flex-direction: column; 566 gap: 28px; 567 width: 100%; 568 max-width: 320px; 569} 570 571/* Quote Block */ 572.quote-block { 573 background: rgba(255, 255, 255, 0.06); 574 border: 1px solid var(--glass-border); 575 border-radius: 18px; 576 padding: 24px; 577 position: relative; 578 backdrop-filter: blur(10px); 579} 580 581.quote-icon { 582 font-size: 32px; 583 color: var(--accent); 584 opacity: 0.5; 585 display: block; 586 margin-bottom: 10px; 587} 588 589.quote-text { 590 font-size: 14px; 591 line-height: 1.7; 592 color: var(--text-main); 593 margin-bottom: 18px; 594 font-style: italic; 595} 596 597.quote-author { 598 display: flex; 599 align-items: center; 600 gap: 12px; 601} 602 603.author-avatar { 604 width: 36px; 605 height: 36px; 606 border-radius: 50%; 607 background: linear-gradient(135deg, var(--accent), var(--accent3)); 608 display: flex; 609 align-items: center; 610 justify-content: center; 611 font-family: var(--font-display); 612 font-size: 12px; 613 font-weight: 700; 614 color: #fff; 615 flex-shrink: 0; 616} 617 618.author-name { 619 font-size: 13px; 620 font-weight: 600; 621 color: var(--text-main); 622} 623 624.author-role { 625 font-size: 11.5px; 626 color: var(--text-sub); 627} 628 629/* Stats Grid */ 630.stats-grid { 631 display: grid; 632 grid-template-columns: 1fr 1fr; 633 gap: 12px; 634} 635 636.stat-card { 637 background: rgba(255, 255, 255, 0.05); 638 border: 1px solid var(--glass-border); 639 border-radius: 14px; 640 padding: 16px 14px; 641 text-align: center; 642 transition: 643 background 0.3s, 644 transform 0.2s; 645 backdrop-filter: blur(6px); 646} 647 648.stat-card:hover { 649 background: rgba(167, 139, 250, 0.1); 650 border-color: rgba(167, 139, 250, 0.3); 651 transform: translateY(-2px); 652} 653 654.stat-card .material-icons-round { 655 font-size: 20px; 656 color: var(--accent); 657 margin-bottom: 6px; 658 display: block; 659} 660 661.stat-num { 662 font-family: var(--font-display); 663 font-size: 18px; 664 font-weight: 800; 665 color: var(--text-main); 666 letter-spacing: -0.5px; 667} 668 669.stat-label { 670 font-size: 11px; 671 color: var(--text-sub); 672 margin-top: 2px; 673} 674 675/* =========================== 676 RESPONSIVE 677=========================== */ 678@media (max-width: 700px) { 679 .wrapper { 680 flex-direction: column; 681 width: 96vw; 682 min-height: unset; 683 border-radius: 20px; 684 } 685 686 .form-panel { 687 width: 100%; 688 padding: 36px 28px; 689 border-right: none; 690 border-bottom: 1px solid var(--glass-border); 691 } 692 693 .right-panel { 694 padding: 32px 24px; 695 } 696 697 .right-inner { 698 max-width: 100%; 699 } 700} 701 702@media (max-width: 400px) { 703 .form-panel { 704 padding: 28px 18px; 705 } 706}
JavaScript
Paste before your closing </body> tag. Handles live validation, password toggle, form submit success state, and social button ripple effect.
js1// =========================== 2// PASSWORD TOGGLE 3// =========================== 4const togglePass = document.getElementById("togglePass"); 5const passwordInput = document.getElementById("password"); 6 7togglePass.addEventListener("click", () => { 8 const isPass = passwordInput.type === "password"; 9 passwordInput.type = isPass ? "text" : "password"; 10 togglePass.textContent = isPass ? "visibility" : "visibility_off"; 11}); 12 13// =========================== 14// HELPERS 15// =========================== 16function setError(inputId, errId, msg) { 17 const box = document.getElementById(inputId)?.closest(".input-box"); 18 const err = document.getElementById(errId); 19 if (box) { 20 box.classList.add("error-state"); 21 box.classList.remove("success-state"); 22 } 23 if (err) err.textContent = msg; 24} 25 26function setSuccess(inputId, errId) { 27 const box = document.getElementById(inputId)?.closest(".input-box"); 28 const err = document.getElementById(errId); 29 if (box) { 30 box.classList.remove("error-state"); 31 box.classList.add("success-state"); 32 } 33 if (err) err.textContent = ""; 34} 35 36function clearState(inputId, errId) { 37 const box = document.getElementById(inputId)?.closest(".input-box"); 38 const err = document.getElementById(errId); 39 if (box) box.classList.remove("error-state", "success-state"); 40 if (err) err.textContent = ""; 41} 42 43// =========================== 44// LIVE VALIDATION 45// =========================== 46const emailRx = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 47 48document.getElementById("email").addEventListener("blur", () => { 49 const val = document.getElementById("email").value.trim(); 50 emailRx.test(val) 51 ? setSuccess("email", "emailErr") 52 : setError("email", "emailErr", "Enter a valid email address."); 53}); 54 55document.getElementById("email").addEventListener("input", () => { 56 const val = document.getElementById("email").value.trim(); 57 if (val.length === 0) clearState("email", "emailErr"); 58}); 59 60document.getElementById("password").addEventListener("input", () => { 61 const val = document.getElementById("password").value; 62 if (val.length === 0) { 63 clearState("password", "passwordErr"); 64 return; 65 } 66 val.length >= 6 67 ? setSuccess("password", "passwordErr") 68 : setError( 69 "password", 70 "passwordErr", 71 "Password must be at least 6 characters.", 72 ); 73}); 74 75// =========================== 76// FORM SUBMIT 77// =========================== 78document.getElementById("loginForm").addEventListener("submit", (e) => { 79 e.preventDefault(); 80 81 const email = document.getElementById("email").value.trim(); 82 const password = document.getElementById("password").value; 83 let valid = true; 84 85 if (!emailRx.test(email)) { 86 setError("email", "emailErr", "Enter a valid email address."); 87 valid = false; 88 } else { 89 setSuccess("email", "emailErr"); 90 } 91 92 if (password.length < 6) { 93 setError( 94 "password", 95 "passwordErr", 96 "Password must be at least 6 characters.", 97 ); 98 valid = false; 99 } else { 100 setSuccess("password", "passwordErr"); 101 } 102 103 if (!valid) return; 104 105 // Success state 106 const btn = document.querySelector(".submit-btn"); 107 btn.classList.add("success-state"); 108 btn.innerHTML = `<span class="material-icons-round">check_circle</span><span>Welcome Back!</span>`; 109 btn.disabled = true; 110}); 111 112// =========================== 113// SOCIAL BUTTON RIPPLE 114// =========================== 115document.querySelectorAll(".social-btn").forEach((btn) => { 116 btn.addEventListener("click", function () { 117 this.style.transform = "scale(0.96)"; 118 setTimeout(() => (this.style.transform = ""), 150); 119 }); 120});
Built by MetaSkillGroup — copy it, use it, make it yours.
Features
Two-panel layout — login form left panel + quote & stats right panel
Glassmorphism card with backdrop-filter: blur and glass border
Three animated floating background blobs (indigo, pink, cyan)
fadeSlideUp entrance animation on page load
Live inline email validation on blur and input
Live password validation (min 6 characters) on inputf
Password visibility toggle
Google and GitHub social login buttons with hover ripple effect
Remember me custom styled checkbox
Zero dependencies — pure HTML, CSS, Vanilla JS
Built With
HTML5
CSS3
JavaScript
Compatibility

Written by
MetaSkillGroup
Guides from our blog
Frontend Developer Roadmap 2026: A Step-by-Step Guide for BeginnersA simple, step-by-step frontend developer roadmap for 2026. Learn HTML, CSS, JavaScript, Git, React, TypeScript and Next.js in the right order, with a 6-month study plan, beginner projects, free resources and a checklist for your first frontend internship.
MERN Stack Roadmap for Beginners 2026: A Step-by-Step Guide to Becoming a Full-Stack DeveloperConfused about where to start with MERN? This step-by-step MERN stack roadmap for 2026 takes you from zero to your first full-stack project. Learn JavaScript, Node.js, Express, MongoDB and React in the right order, with a clear timeline, milestone projects, common mistakes and tips to learn faster with AI tools.
Top 7 React Optimization Techniques to Boost Your App Performance in 2026.Learn the top 7 React optimization techniques including Lazy Loading, Memoization, Debouncing, Throttling, Virtualization, Code Splitting, and Caching to build blazing fast React apps in 2026.
Need it built for you?
Built to the same standard as our client work.
If nothing here fits exactly, tell us what you need — our core developers build it. Requirements call first, a written SRS before any code, and a price agreed up front.