login-signup

Dynamic Login Page with Conditional Hover

This application, contained in the document, implements a unique security feature combined with visual user feedback. Instead of a simple static button, the login process is gamified: the Log In button's visibility and position are directly tied to the user's progress in entering the correct credentials (admin@example.com and password123). The main goal is to prevent the button from being usable until the required inputs are met.

The core mechanism relies on JavaScript tracking the sequential correctness of every character entered in both the email (15 characters) and password (11 characters) fields, totaling a MAX_PROGRESS of 26. As the user types the correct characters, the checkCredentials function calculates a progressRatio between 0 and 1. This ratio is then mapped directly to the button's CSS properties: its transform style (controlling horizontal slide from -100% to 0%) and its opacity (fading it from 20% visible to 100% visible). If a single character is wrong, the progress instantly resets, and the button slides back to its hidden, inactive state.

The key security feature is enforced via the button's final state and its interaction with CSS hover rules. When the credentials are not 100% correct, the button lacks the .active-button class. In this inactive state, the custom CSS rule login-button:hover:not(.active-button) applies, which uses transform: translateX(-120%) and pointer-events: none. This not only slides the button further out of view but, critically, ensures that any mouse clicks pass straight through the hidden button, rendering it physically unpressable until the full set of correct credentials activates it. Once activated, the button is fully visible, clickable, and immune to the hover-hide effect.

Code Snippet

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Conditional Hover Login</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
    
    <style>
        body {
            font-family: 'Inter', sans-serif;
            /* Enhanced background style with a gentle gradient */
            background: linear-gradient(135deg, #e0f2f1 0%, #cfd8dc 100%);
        }

        /* Status message styling */
        .status-box {
            padding: 0.5rem;
            border-radius: 0.5rem;
            transition: all 0.3s ease;
            font-size: 0.875rem; /* text-sm */
        }
        
        /* Transition applies to all states (opacity, transform, shadow) */
        #login-button {
            transition: opacity 0.6s ease-in-out, 
                        transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94), /* Smoother slide transition */
                        box-shadow 0.3s ease;
            
            /* The default is now the initial state set by JS on load, but these serve as a fallback/starting point */
            transform: translateX(-100%); 
            opacity: 0.2; 
            pointer-events: none; /* Cannot be pressed by default/during transition */
        }

        /* Custom CSS to handle the hover logic when INACTIVE:
         * If inactive and hovered, ensure it slides further away and is unpressable.
         * This handles the "disappearance in sliding still" when incorrect.
         */
        #login-button:hover:not(.active-button) {
            transform: translateX(-120%) !important; /* Slide even further left */
            opacity: 0 !important;
            pointer-events: none !important; /* Crucial: Cannot be pressed */
        }
        
        /* Ensures the active button remains visible and interactive - FINAL STATE */
        .active-button {
            cursor: pointer;
            pointer-events: auto !important; /* Re-enable clicking */
            /* Stronger shadow when active to emphasize its readiness */
            box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.5), 0 4px 6px -2px rgba(99, 102, 241, 0.1); 
        }
    </style>
</head>
<body class="flex items-center justify-center min-h-screen p-4">

    <!-- Enhanced Card Styling: Better shadow, rounded edges, and border ring -->
    <div class="w-full max-w-sm bg-white p-8 md:p-10 rounded-3xl shadow-2xl ring-4 ring-indigo-100/50">
        <!-- Header -->
        <h2 class="text-4xl font-extrabold text-gray-900 mb-2 text-center">
            Secure Login
        </h2>
        <p class="text-center text-md text-gray-500 mb-8">
            Access your account (Hint: admin@example.com / password123)
        </p>

        <!-- Status Message Area -->
        <div id="status-message" class="mb-6 h-10 flex items-center justify-center status-box text-gray-500 bg-gray-50">
            Enter credentials to activate the button.
        </div>

        <!-- Login Form -->
        <form id="login-form" class="space-y-6">
            <div>
                <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email address</label>
                <input type="email" id="email" name="email" required
                       class="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:ring-indigo-500 focus:border-indigo-500 transition duration-200 text-gray-800"
                       placeholder="Enter your email">
            </div>

            <div>
                <label for="password" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
                <input type="password" id="password" name="password" required
                       class="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:ring-indigo-500 focus:border-indigo-500 transition duration-200 text-gray-800"
                       placeholder="Enter your password">
            </div>

            <!-- Wrapper div is needed to contain the button's slide and keep it centered when visible -->
            <div class="pt-4 relative">
                <!-- Login Button with Conditional Hover Logic and Gradient Style -->
                <button type="button" id="login-button" 
                        class="w-full flex justify-center py-3 px-4 rounded-xl shadow-lg 
                               text-base font-bold text-white 
                               /* Added a stylish gradient */
                               bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 
                               focus:outline-none focus:ring-4 focus:ring-offset-2 focus:ring-indigo-500/50 
                               transform transition-all duration-300 ease-out">
                    Log In
                </button>
            </div>
        </form>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const emailInput = document.getElementById('email');
            const passwordInput = document.getElementById('password');
            const loginButton = document.getElementById('login-button');
            const statusMessage = document.getElementById('status-message');

            // Define the correct credentials
            const CORRECT_EMAIL = 'admin@example.com'; // Length 15
            const CORRECT_PASSWORD = 'password123'; // Length 11
            const MAX_PROGRESS = CORRECT_EMAIL.length + CORRECT_PASSWORD.length; // 26

            /**
             * Calculates the number of characters entered correctly in sequence from the start.
             * @param {string} inputValue - The current value of the input field.
             * @param {string} correctValue - The required correct value.
             * @returns {number} The count of sequentially correct characters.
             */
            const getCorrectChars = (inputValue, correctValue) => {
                let count = 0;
                // Only check up to the length of the input or the correct string, whichever is shorter.
                const checkLength = Math.min(inputValue.length, correctValue.length);
                
                for (let i = 0; i < checkLength; i++) {
                    if (inputValue[i] === correctValue[i]) {
                        count++;
                    } else {
                        // Stop counting as soon as an incorrect character is found
                        break; 
                    }
                }
                return count;
            };

            /**
             * Checks the credentials, calculates progress, and applies dynamic styles.
             */
            const checkCredentials = () => {
                const emailVal = emailInput.value.trim();
                const passwordVal = passwordInput.value.trim();
                
                // Calculate character-based progress
                const emailProgress = getCorrectChars(emailVal, CORRECT_EMAIL);
                const passwordProgress = getCorrectChars(passwordVal, CORRECT_PASSWORD);
                
                let totalProgress = emailProgress + passwordProgress;
                
                // Calculate the ratio (0.0 to 1.0)
                const progressRatio = Math.min(1, totalProgress / MAX_PROGRESS);

                // --- Apply Progressive Style (Slide and Opacity) ---
                
                // 1. Transform (slide): from -100% (hidden) to 0% (visible)
                const translateX = -100 + (progressRatio * 100);
                loginButton.style.transform = `translateX(${translateX}%)`;

                // 2. Opacity: from 0.2 (initial hidden state) to 1.0 (fully active)
                const opacity = 0.2 + (progressRatio * 0.8);
                loginButton.style.opacity = opacity;


                // --- Check for Final Activation ---
                const isFullyCorrect = (
                    emailVal === CORRECT_EMAIL &&
                    passwordVal === CORRECT_PASSWORD
                );

                if (isFullyCorrect) {
                    // Final state: Apply 'active-button' class to enable clicking and override inactive hover rules
                    loginButton.classList.add('active-button');
                    loginButton.textContent = "ACCESS GRANTED";
                    
                    statusMessage.textContent = "✓ Success! Button is fully active.";
                    statusMessage.className = "mb-6 h-10 flex items-center justify-center status-box bg-green-100 text-green-700 border border-green-300";
                    
                } else {
                    // Non-final state: Ensure 'active-button' is removed
                    loginButton.classList.remove('active-button');
                    loginButton.textContent = "Log In";
                    
                    if (totalProgress > 0) {
                        statusMessage.textContent = `Progress: ${totalProgress}/${MAX_PROGRESS} characters correct. Keep typing!`;
                        statusMessage.className = "mb-6 h-10 flex items-center justify-center status-box bg-yellow-100 text-yellow-700 border border-yellow-300";
                    } else {
                        statusMessage.textContent = "Enter credentials to begin activation.";
                        statusMessage.className = "mb-6 h-10 flex items-center justify-center status-box text-gray-500 bg-gray-50";
                    }
                }
            };

            /**
             * Handles the actual login attempt when the button is clicked.
             */
            const handleLogin = () => {
                if (loginButton.classList.contains('active-button')) {
                    statusMessage.textContent = "🎉 Login Successful! Welcome back.";
                    statusMessage.className = "mb-6 h-10 flex items-center justify-center status-box bg-indigo-100 text-indigo-700 border border-indigo-400 font-bold";
                    
                } else {
                    statusMessage.textContent = "Access denied. Button is inactive.";
                    statusMessage.className = "mb-6 h-10 flex items-center justify-center status-box bg-red-100 text-red-700 border border-red-300";
                }
            };


            // 1. Attach the core checking function to input changes
            emailInput.addEventListener('input', checkCredentials);
            passwordInput.addEventListener('input', checkCredentials);
            
            // 2. Attach the login handler to the button click
            loginButton.addEventListener('click', handleLogin);

            // Initial check
            checkCredentials();
        });
    </script>
</body>
</html>
Resource ID: #00008
Posted: December 16, 2025
© 2025 Your Code Library.