AURA AUTH – Segmented Control Tabs Login / Register UI (Tailwind Glassmorphism)
Front‑end UI demo only. This is a modern login/register switch with a sliding segmented control, glassmorphism styling, password strength meter, show/hide password, forgot‑password modal, and toast notifications. It uses Tailwind CDN and plain JavaScript. No backend authentication is included. Use it as a starting point and connect your own secure server logic.
🔥 UI Features: Segmented control with sliding indicator, glassmorphism + glowing background orbs, Tailwind CDN, password strength meter, show/hide password, forgot‑password modal, toast messages, social‑login buttons (UI only), 100% responsive.
Why Use Segmented Control Tabs?
- ✅ Switch between Login and Register in one screen – no page reload
- ✅ Familiar pattern used by many fintech and banking apps
- ✅ No npm or build step – works instantly with CDN links
- ✅ Easy to customise colours, spacing, and copy
Full Source Code – Copy and Save as aura-auth.html
Complete front‑end code (HTML + Tailwind config + CSS + JavaScript):
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aura Auth - Modern Glassmorphism Portal</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome Icons CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts Inter -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
glass: {
bg: 'rgba(255, 255, 255, 0.08)',
border: 'rgba(255, 255, 255, 0.18)',
input: 'rgba(255, 255, 255, 0.05)',
hover: 'rgba(255, 255, 255, 0.15)',
}
},
animation: {
'float-slow': 'float 18s ease-in-out infinite',
'float-reverse': 'floatReverse 22s ease-in-out infinite',
'pulse-glow': 'pulseGlow 6s ease-in-out infinite',
},
keyframes: {
float: {
'0%, 100%': { transform: 'translate(0, 0) scale(1)' },
'50%': { transform: 'translate(60px, -40px) scale(1.1)' },
},
floatReverse: {
'0%, 100%': { transform: 'translate(0, 0) scale(1)' },
'50%': { transform: 'translate(-50px, 50px) scale(0.95)' },
},
pulseGlow: {
'0%, 100%': { opacity: 0.6 },
'50%': { opacity: 0.9 },
}
}
}
}
}
</script>
<style>
/* Custom Glassmorphism styles */
.glass-card {
background: rgba(20, 24, 40, 0.55);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 30px 60px -12px rgba(0, 0, 0, 0.5),
0 18px 36px -18px rgba(0, 0, 0, 0.4),
inset 0 1px 1px 0 rgba(255, 255, 255, 0.2);
}
.glass-input {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-input:focus {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(168, 85, 247, 0.7);
box-shadow: 0 0 20px rgba(168, 85, 247, 0.35),
inset 0 0 10px rgba(168, 85, 247, 0.1);
}
.glass-button {
background: linear-gradient(135deg, rgba(168, 85, 247, 0.8), rgba(99, 102, 241, 0.8));
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow: 0 10px 25px -5px rgba(147, 51, 234, 0.5);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-button:hover {
background: linear-gradient(135deg, rgba(168, 85, 247, 0.95), rgba(99, 102, 241, 0.95));
box-shadow: 0 15px 30px -5px rgba(147, 51, 234, 0.7);
transform: translateY(-2px);
}
.glass-button:active {
transform: translateY(0);
}
.social-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.12);
transition: all 0.3s ease;
}
.social-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.6);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
/* Form transition animations */
.form-fade-enter {
animation: fadeInScale 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes fadeInScale {
0% {
opacity: 0;
transform: scale(0.96) translateY(8px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
</style>
</head>
<body class="h-full bg-slate-950 font-sans text-slate-100 flex items-center justify-center min-h-screen overflow-x-hidden p-4 sm:p-6 relative">
<!-- Ambient Glowing Gradient Orbs (Background Animation) -->
<div class="fixed inset-0 overflow-hidden pointer-events-none z-0">
<!-- Top Left Cyan/Indigo Orb -->
<div class="absolute -top-32 -left-32 w-96 h-96 sm:w-[500px] sm:h-[500px] bg-gradient-to-tr from-cyan-500/30 to-indigo-600/40 rounded-full blur-3xl animate-float-slow opacity-70"></div>
<!-- Bottom Right Purple/Pink Orb -->
<div class="absolute -bottom-32 -right-32 w-96 h-96 sm:w-[550px] sm:h-[550px] bg-gradient-to-br from-purple-600/35 to-fuchsia-600/35 rounded-full blur-3xl animate-float-reverse opacity-70"></div>
<!-- Center Blue Glow -->
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 h-80 sm:w-[450px] sm:h-[450px] bg-blue-600/20 rounded-full blur-[100px] animate-pulse-glow pointer-events-none"></div>
<!-- Subtle Grid Pattern Overlay -->
<div class="absolute inset-0 bg-[radial-gradient(rgba(255,255,255,0.05)_1px,transparent_1px)] [background-size:24px_24px] opacity-40"></div>
</div>
<!-- Main Container Card -->
<main class="relative z-10 w-full max-w-md">
<div class="glass-card rounded-3xl p-6 sm:p-8 relative overflow-hidden transition-all duration-500">
<!-- Top Branding & Logo -->
<div class="text-center mb-6">
<div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-gradient-to-tr from-purple-500/30 to-indigo-500/30 border border-white/20 mb-3 shadow-lg shadow-purple-500/10 backdrop-blur-md">
<i class="fa-solid fa-cube text-2xl text-purple-300"></i>
</div>
<h1 class="text-2xl sm:text-3xl font-bold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-white via-slate-100 to-slate-400">
Start From Here
</h1>
<p id="form-subtitle" class="text-xs sm:text-sm text-slate-400 mt-1 transition-all duration-300">
Welcome back! Please sign in to continue.
</p>
</div>
<!-- Segmented Control Tabs (Switch Login / Register) -->
<div class="grid grid-cols-2 p-1 mb-6 rounded-2xl bg-slate-900/40 border border-white/10 relative">
<!-- Sliding Indicator -->
<div id="tab-indicator" class="absolute top-1 bottom-1 left-1 w-[calc(50%-4px)] bg-white/15 rounded-xl border border-white/15 transition-all duration-300 ease-out shadow-sm"></div>
<button id="btn-tab-login" onclick="switchTab('login')" class="relative z-10 py-2.5 text-xs sm:text-sm font-semibold rounded-xl text-white transition-colors duration-200 focus:outline-none">
Sign In
</button>
<button id="btn-tab-register" onclick="switchTab('register')" class="relative z-10 py-2.5 text-xs sm:text-sm font-semibold rounded-xl text-slate-400 hover:text-white transition-colors duration-200 focus:outline-none">
Sign Up
</button>
</div>
<!-- Dynamic Notification Toast Container -->
<div id="toast" class="hidden mb-4 p-3 rounded-xl border text-xs sm:text-sm flex items-center space-x-2 transition-all duration-300">
<i id="toast-icon" class="fa-solid"></i>
<span id="toast-msg" class="flex-1"></span>
</div>
<!-- LOGIN FORM -->
<form id="form-login" class="space-y-4 form-fade-enter" onsubmit="handleLoginSubmit(event)" novalidate>
<!-- Email or Username Field -->
<div class="space-y-1">
<label for="login-email" class="block text-xs font-medium text-slate-300">Email address</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<i class="fa-regular fa-envelope text-sm"></i>
</div>
<input type="email" id="login-email" required
class="glass-input w-full pl-10 pr-4 py-3 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="name@company.com">
</div>
<span id="err-login-email" class="text-[11px] text-rose-400 hidden pl-1">Please enter a valid email address.</span>
</div>
<!-- Password Field -->
<div class="space-y-1">
<div class="flex items-center justify-between">
<label for="login-pass" class="block text-xs font-medium text-slate-300">Password</label>
<button type="button" onclick="openForgotModal()" class="text-xs text-purple-400 hover:text-purple-300 transition-colors focus:outline-none">
Forgot password?
</button>
</div>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<i class="fa-solid fa-lock text-sm"></i>
</div>
<input type="password" id="login-pass" required
class="glass-input w-full pl-10 pr-10 py-3 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="••••••••">
<button type="button" onclick="togglePasswordVisibility('login-pass', 'eye-icon-login')" class="absolute inset-y-0 right-0 pr-3.5 flex items-center text-slate-400 hover:text-slate-200 transition-colors focus:outline-none">
<i id="eye-icon-login" class="fa-regular fa-eye text-sm"></i>
</button>
</div>
<span id="err-login-pass" class="text-[11px] text-rose-400 hidden pl-1">Password is required.</span>
</div>
<!-- Remember Me & Options -->
<div class="flex items-center justify-between pt-1">
<label class="flex items-center space-x-2.5 cursor-pointer group">
<div class="relative flex items-center">
<input type="checkbox" id="remember-me" class="sr-only peer">
<div class="w-4 h-4 rounded border border-white/20 bg-white/5 peer-checked:bg-purple-600 peer-checked:border-purple-500 transition-all"></div>
<i class="fa-solid fa-check text-[10px] text-white absolute inset-0 m-auto opacity-0 peer-checked:opacity-100 transition-opacity"></i>
</div>
<span class="text-xs text-slate-300 group-hover:text-white transition-colors">Remember me for 30 days</span>
</label>
</div>
<!-- Submit Button -->
<button type="submit" id="btn-login-submit" class="glass-button w-full py-3.5 rounded-xl font-semibold text-sm text-white flex items-center justify-center space-x-2 relative overflow-hidden mt-2">
<span id="btn-login-text">Sign In</span>
<i id="btn-login-spinner" class="fa-solid fa-circle-notch fa-spin text-sm hidden"></i>
</button>
</form>
<!-- REGISTER FORM -->
<form id="form-register" class="space-y-4 hidden form-fade-enter" onsubmit="handleRegisterSubmit(event)" novalidate>
<!-- Full Name Field -->
<div class="space-y-1">
<label for="reg-name" class="block text-xs font-medium text-slate-300">Full Name</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<i class="fa-regular fa-user text-sm"></i>
</div>
<input type="text" id="reg-name" required
class="glass-input w-full pl-10 pr-4 py-3 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="Alex Morgan">
</div>
<span id="err-reg-name" class="text-[11px] text-rose-400 hidden pl-1">Please enter your full name.</span>
</div>
<!-- Email Field -->
<div class="space-y-1">
<label for="reg-email" class="block text-xs font-medium text-slate-300">Email address</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<i class="fa-regular fa-envelope text-sm"></i>
</div>
<input type="email" id="reg-email" required
class="glass-input w-full pl-10 pr-4 py-3 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="name@company.com">
</div>
<span id="err-reg-email" class="text-[11px] text-rose-400 hidden pl-1">Please enter a valid email address.</span>
</div>
<!-- Password Field with Strength Indicator -->
<div class="space-y-1">
<label for="reg-pass" class="block text-xs font-medium text-slate-300">Create Password</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<i class="fa-solid fa-lock text-sm"></i>
</div>
<input type="password" id="reg-pass" oninput="checkPasswordStrength(this.value)" required
class="glass-input w-full pl-10 pr-10 py-3 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="At least 8 characters">
<button type="button" onclick="togglePasswordVisibility('reg-pass', 'eye-icon-reg')" class="absolute inset-y-0 right-0 pr-3.5 flex items-center text-slate-400 hover:text-slate-200 transition-colors focus:outline-none">
<i id="eye-icon-reg" class="fa-regular fa-eye text-sm"></i>
</button>
</div>
<!-- Password Strength Bar -->
<div class="pt-1.5 space-y-1">
<div class="flex h-1.5 w-full bg-slate-800 rounded-full overflow-hidden space-x-1 p-0.5">
<div id="strength-1" class="h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300"></div>
<div id="strength-2" class="h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300"></div>
<div id="strength-3" class="h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300"></div>
<div id="strength-4" class="h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300"></div>
</div>
<div class="flex justify-between items-center text-[10px]">
<span class="text-slate-400">Strength:</span>
<span id="strength-text" class="font-medium text-slate-400">Too weak</span>
</div>
</div>
</div>
<!-- Terms and Conditions Checkbox -->
<div class="flex items-start space-x-2.5 pt-1">
<div class="relative flex items-center mt-0.5">
<input type="checkbox" id="accept-terms" required class="sr-only peer">
<div class="w-4 h-4 rounded border border-white/20 bg-white/5 peer-checked:bg-purple-600 peer-checked:border-purple-500 transition-all"></div>
<i class="fa-solid fa-check text-[10px] text-white absolute inset-0 m-auto opacity-0 peer-checked:opacity-100 transition-opacity"></i>
</div>
<label for="accept-terms" class="text-xs text-slate-300 cursor-pointer">
I agree to the <a href="#" class="text-purple-400 hover:underline">Terms of Service</a> and <a href="#" class="text-purple-400 hover:underline">Privacy Policy</a>
</label>
</div>
<span id="err-reg-terms" class="text-[11px] text-rose-400 hidden pl-1">You must accept terms to continue.</span>
<!-- Submit Button -->
<button type="submit" id="btn-reg-submit" class="glass-button w-full py-3.5 rounded-xl font-semibold text-sm text-white flex items-center justify-center space-x-2 relative overflow-hidden mt-2">
<span id="btn-reg-text">Create Account</span>
<i id="btn-reg-spinner" class="fa-solid fa-circle-notch fa-spin text-sm hidden"></i>
</button>
</form>
<!-- Divider -->
<div class="relative my-6">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-white/10"></div>
</div>
<div class="relative flex justify-center text-xs uppercase">
<span class="bg-slate-900/60 backdrop-blur-md px-3 text-slate-400 rounded-full border border-white/5">
Or continue with
</span>
</div>
</div>
<!-- Social Logins (UI only) -->
<div class="grid grid-cols-3 gap-3">
<button type="button" onclick="handleSocialLogin('Google')" class="social-btn flex items-center justify-center py-2.5 rounded-xl focus:outline-none" title="Google login (demo)">
<svg class="w-5 h-5" viewBox="0 0 24 24">
<path fill="#EA4335" d="M12 5c1.6 0 3 .6 4.1 1.6l3.1-3.1C17.3 1.7 14.8 1 12 1 7.5 1 3.7 3.6 1.9 7.3l3.7 2.9C6.5 7.3 9 5 12 5z"/>
<path fill="#4285F4" d="M23.5 12.3c0-.8-.1-1.6-.2-2.3H12v4.5h6.5c-.3 1.5-1.1 2.8-2.4 3.7l3.7 2.9c2.2-2 3.7-5 3.7-8.8z"/>
<path fill="#FBBC05" d="M5.6 14.8c-.2-.7-.4-1.5-.4-2.3s.2-1.6.4-2.3L1.9 7.3C.7 9.7 0 10.8 0 12.5s.7 2.8 1.9 5.2l3.7-2.9z"/>
<path fill="#34A853" d="M12 23c3.2 0 6-1.1 8-3l-3.7-2.9c-1.1.7-2.5 1.2-4.3 1.2-3 0-5.5-2.3-6.4-5.2L1.9 16C3.7 19.7 7.5 23 12 23z"/>
</svg>
</button>
<button type="button" onclick="handleSocialLogin('GitHub')" class="social-btn flex items-center justify-center py-2.5 rounded-xl focus:outline-none" title="GitHub login (demo)">
<i class="fa-brands fa-github text-lg text-white"></i>
</button>
<button type="button" onclick="handleSocialLogin('Apple')" class="social-btn flex items-center justify-center py-2.5 rounded-xl focus:outline-none" title="Apple login (demo)">
<i class="fa-brands fa-apple text-xl text-white"></i>
</button>
</div>
<!-- Honest footer note -->
<p class="text-center text-[11px] text-slate-400 mt-6">
UI demo only — not connected to a backend.
</p>
</div>
</main>
<!-- Forgot Password Modal -->
<div id="modal-forgot" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md opacity-0 pointer-events-none transition-all duration-300">
<div class="glass-card w-full max-w-sm rounded-2xl p-6 relative transform scale-95 transition-all duration-300" id="modal-card">
<button type="button" onclick="closeForgotModal()" class="absolute top-4 right-4 text-slate-400 hover:text-white transition-colors">
<i class="fa-solid fa-xmark text-lg"></i>
</button>
<div class="text-center mb-5">
<div class="inline-flex items-center justify-center w-12 h-12 rounded-xl bg-purple-500/20 text-purple-300 border border-purple-500/30 mb-3">
<i class="fa-solid fa-key text-lg"></i>
</div>
<h3 class="text-lg font-bold text-white">Reset Password</h3>
<p class="text-xs text-slate-400 mt-1">Enter your email and we'll send you a link to reset your password.</p>
</div>
<form onsubmit="handleForgotSubmit(event)" class="space-y-4">
<div class="space-y-1">
<label for="forgot-email" class="block text-xs font-medium text-slate-300">Email address</label>
<input type="email" id="forgot-email" required
class="glass-input w-full px-3.5 py-2.5 rounded-xl text-sm text-white placeholder-slate-500 focus:outline-none"
placeholder="name@company.com">
</div>
<button type="submit" id="btn-forgot-submit" class="glass-button w-full py-2.5 rounded-xl font-semibold text-sm text-white flex items-center justify-center space-x-2">
<span id="btn-forgot-text">Send Reset Link</span>
<i id="btn-forgot-spinner" class="fa-solid fa-circle-notch fa-spin text-sm hidden"></i>
</button>
</form>
</div>
</div>
<!-- Interactive Logic JavaScript -->
<script>
// Tab switching logic
let currentTab = 'login';
function switchTab(tab) {
if (currentTab === tab) return;
currentTab = tab;
const indicator = document.getElementById('tab-indicator');
const loginBtn = document.getElementById('btn-tab-login');
const regBtn = document.getElementById('btn-tab-register');
const loginForm = document.getElementById('form-login');
const regForm = document.getElementById('form-register');
const subtitle = document.getElementById('form-subtitle');
hideToast();
if (tab === 'login') {
indicator.style.transform = 'translateX(0%)';
loginBtn.classList.add('text-white');
loginBtn.classList.remove('text-slate-400');
regBtn.classList.add('text-slate-400');
regBtn.classList.remove('text-white');
loginForm.classList.remove('hidden');
regForm.classList.add('hidden');
subtitle.textContent = "Welcome back! Please sign in to continue.";
} else {
indicator.style.transform = 'translateX(100%)';
regBtn.classList.add('text-white');
regBtn.classList.remove('text-slate-400');
loginBtn.classList.add('text-slate-400');
loginBtn.classList.remove('text-white');
regForm.classList.remove('hidden');
loginForm.classList.add('hidden');
subtitle.textContent = "Create an account to get started with Aura.";
}
}
// Toggle Password Visibility
function togglePasswordVisibility(inputId, iconId) {
const input = document.getElementById(inputId);
const icon = document.getElementById(iconId);
if (input.type === 'password') {
input.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
input.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
// Password Strength Indicator Logic
function checkPasswordStrength(val) {
const s1 = document.getElementById('strength-1');
const s2 = document.getElementById('strength-2');
const s3 = document.getElementById('strength-3');
const s4 = document.getElementById('strength-4');
const label = document.getElementById('strength-text');
let score = 0;
if (!val) {
resetStrength();
return;
}
if (val.length >= 8) score++;
if (/[A-Z]/.test(val) && /[a-z]/.test(val)) score++;
if (/[0-9]/.test(val)) score++;
if (/[^A-Za-z0-9]/.test(val)) score++;
// Reset classes
[s1, s2, s3, s4].forEach(el => el.className = 'h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300');
switch (score) {
case 1:
s1.classList.replace('bg-slate-700', 'bg-rose-500');
label.textContent = 'Weak';
label.className = 'font-medium text-rose-400';
break;
case 2:
s1.classList.replace('bg-slate-700', 'bg-amber-500');
s2.classList.replace('bg-slate-700', 'bg-amber-500');
label.textContent = 'Fair';
label.className = 'font-medium text-amber-400';
break;
case 3:
s1.classList.replace('bg-slate-700', 'bg-blue-500');
s2.classList.replace('bg-slate-700', 'bg-blue-500');
s3.classList.replace('bg-slate-700', 'bg-blue-500');
label.textContent = 'Good';
label.className = 'font-medium text-blue-400';
break;
case 4:
s1.classList.replace('bg-slate-700', 'bg-emerald-500');
s2.classList.replace('bg-slate-700', 'bg-emerald-500');
s3.classList.replace('bg-slate-700', 'bg-emerald-500');
s4.classList.replace('bg-slate-700', 'bg-emerald-500');
label.textContent = 'Strong';
label.className = 'font-medium text-emerald-400';
break;
default:
resetStrength();
}
}
function resetStrength() {
[1, 2, 3, 4].forEach(i => {
const el = document.getElementById(`strength-${i}`);
el.className = 'h-full w-1/4 rounded-full bg-slate-700 transition-colors duration-300';
});
const label = document.getElementById('strength-text');
label.textContent = 'Too weak';
label.className = 'font-medium text-slate-400';
}
// Notification Toast Handler
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
const icon = document.getElementById('toast-icon');
const msg = document.getElementById('toast-msg');
toast.classList.remove('hidden', 'bg-emerald-500/10', 'border-emerald-500/30', 'text-emerald-300', 'bg-rose-500/10', 'border-rose-500/30', 'text-rose-300');
icon.className = 'fa-solid';
if (type === 'success') {
toast.classList.add('bg-emerald-500/10', 'border-emerald-500/30', 'text-emerald-300');
icon.classList.add('fa-circle-check');
} else {
toast.classList.add('bg-rose-500/10', 'border-rose-500/30', 'text-rose-300');
icon.classList.add('fa-circle-exclamation');
}
msg.textContent = message;
}
function hideToast() {
const toast = document.getElementById('toast');
toast.classList.add('hidden');
}
// Handle Login Submission (simulated)
function handleLoginSubmit(e) {
e.preventDefault();
hideToast();
const email = document.getElementById('login-email');
const pass = document.getElementById('login-pass');
const errEmail = document.getElementById('err-login-email');
const errPass = document.getElementById('err-login-pass');
let isValid = true;
if (!email.value || !validateEmail(email.value)) {
errEmail.classList.remove('hidden');
isValid = false;
} else {
errEmail.classList.add('hidden');
}
if (!pass.value) {
errPass.classList.remove('hidden');
isValid = false;
} else {
errPass.classList.add('hidden');
}
if (!isValid) return;
// Trigger Loading State
setButtonLoading('btn-login-submit', 'btn-login-text', 'btn-login-spinner', true);
setTimeout(() => {
setButtonLoading('btn-login-submit', 'btn-login-text', 'btn-login-spinner', false);
showToast('Demo: login successful (no backend).', 'success');
}, 1800);
}
// Handle Registration Submission (simulated)
function handleRegisterSubmit(e) {
e.preventDefault();
hideToast();
const name = document.getElementById('reg-name');
const email = document.getElementById('reg-email');
const terms = document.getElementById('accept-terms');
const errName = document.getElementById('err-reg-name');
const errEmail = document.getElementById('err-reg-email');
const errTerms = document.getElementById('err-reg-terms');
let isValid = true;
if (!name.value.trim()) {
errName.classList.remove('hidden');
isValid = false;
} else {
errName.classList.add('hidden');
}
if (!email.value || !validateEmail(email.value)) {
errEmail.classList.remove('hidden');
isValid = false;
} else {
errEmail.classList.add('hidden');
}
if (!terms.checked) {
errTerms.classList.remove('hidden');
isValid = false;
} else {
errTerms.classList.add('hidden');
}
if (!isValid) return;
// Trigger Loading State
setButtonLoading('btn-reg-submit', 'btn-reg-text', 'btn-reg-spinner', true);
setTimeout(() => {
setButtonLoading('btn-reg-submit', 'btn-reg-text', 'btn-reg-spinner', false);
showToast('Demo: account created (no backend).', 'success');
}, 2000);
}
// Social Login Handler (UI only)
function handleSocialLogin(provider) {
showToast(`Demo: ${provider} login is not connected.`, 'success');
}
// Utility: Set Loading Button
function setButtonLoading(btnId, textId, spinnerId, isLoading) {
const btn = document.getElementById(btnId);
const text = document.getElementById(textId);
const spinner = document.getElementById(spinnerId);
if (isLoading) {
btn.disabled = true;
btn.classList.add('opacity-80', 'cursor-not-allowed');
text.classList.add('opacity-0');
spinner.classList.remove('hidden');
} else {
btn.disabled = false;
btn.classList.remove('opacity-80', 'cursor-not-allowed');
text.classList.remove('opacity-0');
spinner.classList.add('hidden');
}
}
// Forgot Password Modal Control
function openForgotModal() {
const modal = document.getElementById('modal-forgot');
const card = document.getElementById('modal-card');
modal.classList.remove('opacity-0', 'pointer-events-none');
card.classList.remove('scale-95');
card.classList.add('scale-100');
}
function closeForgotModal() {
const modal = document.getElementById('modal-forgot');
const card = document.getElementById('modal-card');
modal.classList.add('opacity-0', 'pointer-events-none');
card.classList.remove('scale-100');
card.classList.add('scale-95');
}
function handleForgotSubmit(e) {
e.preventDefault();
const email = document.getElementById('forgot-email').value;
if (!email || !validateEmail(email)) return;
setButtonLoading('btn-forgot-submit', 'btn-forgot-text', 'btn-forgot-spinner', true);
setTimeout(() => {
setButtonLoading('btn-forgot-submit', 'btn-forgot-text', 'btn-forgot-spinner', false);
closeForgotModal();
showToast(`Demo: reset link would be sent to ${email} (no backend).`, 'success');
}, 1500);
}
// Email Validation Helper
function validateEmail(email) {
return String(email)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
}
</script>
</body>
</html>
How to Adapt This UI for a Real Backend
This file is front‑end only. The login, registration, forgot‑password, and social buttons use simulated JavaScript (setTimeout). To make it real:
- Replace
handleLoginSubmitwith afetch()POST to your login endpoint. - Replace
handleRegisterSubmitwith afetch()POST to your registration endpoint. - Replace
handleForgotSubmitwith a real password‑reset request. - Add CSRF protection, server‑side validation, rate limiting, and secure password hashing (e.g., bcrypt/Argon2).
- Remove or implement the social‑login buttons — they are UI placeholders.
- Add real error handling and redirects on success.
Limitations (Read Before Using)
- No backend authentication – forms only show demo toasts.
- No real reCAPTCHA – the footer no longer claims reCAPTCHA protection.
- Social login buttons are non‑functional – they only display a toast.
- Modal accessibility – focus trap, Escape key, and backdrop‑click close are not implemented.
- Toast accessibility – no
role="status"oraria-live. - Not production‑ready – use as a UI starting point, then add secure server logic.
Tags
segmented control tabs, login register switch, glassmorphism login UI, tailwind auth ui demo, front-end login template, free tailwind css
0 Comments: