Skip to content
UI ComponentLogin FormForm

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
Glassmorphism login form with social auth: welcome back form, Google and GitHub buttons and a testimonial panel

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.

🔗 View Live Demo


What's Included

  • 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 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

NameVersionType
Google Fonts — Syne + DM SanslatestCDN
Material Icons RoundlatestCDN

Copy all CDN links from the <head> of index.html and paste into your project before your stylesheet.


Desktop Preview

Desktop preview of a glassmorphism login form with email, password, Google and GitHub sign-in beside a stats panel


HTML

Copy the full markup — background blob divs, wrapper, form panel, and right panel — and paste inside your <body>.

html
1<!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.

css
1/* ===========================
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.

js
1// ===========================
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

Browsers
ChromeFirefoxBraveArcVivaldiSafariOperaEdge
Devices
DesktopTabletMobileSmartwatchTV
Frameworks
Vanilla JSHTML/CSS
MetaSkillGroup

Written by

MetaSkillGroup

August 22, 2026

Guides from our blog

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.