Segmented Control Tabs for Login / Register Switch - Free Code 2026

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:

  1. Replace handleLoginSubmit with a fetch() POST to your login endpoint.
  2. Replace handleRegisterSubmit with a fetch() POST to your registration endpoint.
  3. Replace handleForgotSubmit with a real password‑reset request.
  4. Add CSRF protection, server‑side validation, rate limiting, and secure password hashing (e.g., bcrypt/Argon2).
  5. Remove or implement the social‑login buttons — they are UI placeholders.
  6. 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" or aria-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

Share

0 Comments: