Quite some time ago, when I first designed a login page, I treated it as a simple form with username and password fields. It was functional but unattractive, and didn’t particularly inspire confidence. The web was quite different back then, but nowadays that basic approach would have a terrible impact on user experience and conversion rates. Today, a well-designed login page is absolutely essential for establishing trust and providing a positive first impression.

In this guide, I’ll walk you through the creation of a modern, responsive login page with HTML, CSS, and JS. I’ll share code examples, design considerations, and some lessons I’ve learned from my own projects.

Why UI/UX of the Login Pages Matters

A login page is often the first meaningful interaction a user has with the applications we build. Think about the last time you encountered a poorly designed login page. Perhaps the fields were too small on your phone, or you ran into confusing error messages. These seemingly small issues can frustrate users before they even access your content.

It’s important to design a good login page because it:

  • Creates a positive first impression
  • Builds trust with users
  • Reduces friction in the authentication process
  • Reinforces brand identity
  • Improves accessibility for all users

HTML Structure for Login Forms

A well-structured login form provides a solid foundation for both functionality and styling. A good HTML structure for our modern login page could look like this:

<!DOCTYPE html> <html lang=“en”> <head>     <!– Character encoding for proper text display –>     <meta charset=“UTF-8”>     <!– Viewport meta tag for responsive design on mobile devices –>     <meta name=“viewport” content=“width=device-width, initial-scale=1.0”>     <!– Page title displayed in browser tab –>     <title>Modern Login Page</title>     <!– Link to external CSS stylesheet –>     <link rel=“stylesheet” href=“styles.css”> </head> <body>     <!– Main container for the entire login form –>     <div class=“login-container”>         <!– Header section with welcome message –>         <div class=“login-header”>             <h1>Welcome Back</h1>             <!– Subtitle providing instructions to the user –>             <p>Enter your credentials to access your account</p>         </div>                 <!– Main login form –>         <form class=“login-form”>             <!– Username/Email input group –>             <div class=“form-group”>                 <!– Label for username field (connected via ‘for’ attribute for accessibility) –>                 <label for=“username”>Username or Email</label>                 <!– Text input for username/email with required validation –>                 <input type=“text” id=“username” name=“username” required>             </div>                         <!– Password input group –>             <div class=“form-group”>                 <!– Label for password field –>                 <label for=“password”>Password</label>                 <!– Container for password field and visibility toggle button –>                 <div class=“password-field”>                     <!– Password input field with required validation –>                     <input type=“password” id=“password” name=“password” required>                     <!– Button to toggle password visibility –>                     <button type=“button” class=“toggle-password” aria-label=“Toggle password visibility”>                         <!– SVG icon for eye (shows/hides password) –>                         <svg class=“eye-icon” xmlns=“http://www.w3.org/2000/svg” viewBox=“0 0 24 24” width=“24” height=“24” fill=“currentColor”>                             <!– Open eye icon (shown when password is hidden) –>                             <path class=“eye-open” d=“M12 4.5c-5 0-9.27 3.11-11 7.5 1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z”/>                                                         <!– Closed/crossed eye icon (shown when password is visible) –>                             <path class=“eye-closed” d=“M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z” style=“display: none;”/>                         </svg>                     </button>                 </div>                 <!– Error message container (hidden by default, shown via JavaScript when validation fails) –>                 <div class=“error-message” style=“display: none;”>Password must be at least 6 characters</div>             </div>                         <!– Container for form options (Remember me and Forgot password) –>             <div class=“form-options”>                 <!– Remember me checkbox section –>                 <div class=“remember-me”>                     <!– Checkbox for “Remember me” functionality –>                     <input type=“checkbox” id=“remember” name=“remember”>                     <!– Label for remember me checkbox –>                     <label for=“remember”>Remember me</label>                 </div>                 <!– Link to password recovery page –>                 <a href=“#” class=“forgot-password”>Forgot password?</a>             </div>                         <!– Submit button for the login form –>             <button type=“submit” class=“login-button”>Log In</button>                         <!– Registration link section for new users –>             <div class=“register-link”>                 <!– Text with link to registration page –>                 <p>Don’t have an account? <a href=“#”>Sign up</a></p>             </div>         </form>     </div>         <!– Link to external JavaScript file for form functionality –>     <script src=“script.js”></script> </body> </html>

The key HTML elements I’ve included are:

  • A container for the entire login form
  • Clear header with welcoming text
  • Form groups for each input field
  • Password field with visibility toggle
  • “Remember me” checkbox 
  • “Forgot password” link
  • Submit button
  • Registration link for new users

With this, we now have a form that could technically work but that will look like it’s broken to most users online. We will fix that by adding some CSS styles.


Figure 1. Unstyled HTML login form showing basic structure

Styling with CSS

In order to transform our plain HTML into a clean, modern design, we’ll add some CSS styles:

/* CSS Reset and Base Styles */ * {   margin: 0;   padding: 0;   box-sizing: border-box; /* Ensures padding and borders are included in element width/height */   font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif; } /* Full height body with centered content */ body {   height: 100vh; /* Full viewport height */   display: flex;   align-items: center; /* Center vertically */   justify-content: center; /* Center horizontally */   background: linear-gradient(120deg, #a1c4fd, #c2e9fb); /* Soft blue gradient background */ } /* Main login container */ .login-container {   background-color: white;   border-radius: 10px;   box-shadow: 0 14px 28px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.05); /* Layered shadows for depth */   width: 400px;   max-width: 90%; /* Responsive width on smaller screens */   padding: 2rem; } /* Header section styling */ .login-header {   margin-bottom: 2rem;   text-align: center; } .login-header h1 {   color: #333;   margin-bottom: 0.5rem;   font-size: 2.5rem; } .login-header p {   color: #777; /* Lighter color for subtitle */   font-size: 1rem; } /* Form group container */ .form-group {   margin-bottom: 1.5rem; /* Space between form elements */ } /* Label styling */ .form-group label {   display: block;   margin-bottom: 0.5rem;   color: #555;   font-size: 1rem; } /* Input field styling */ .form-group input[type=”text”], .form-group input[type=”password”] {   width: 100%;   padding: 0.8rem;   border: 1px solid #ddd;   border-radius: 4px;   transition: border-color 0.3s, box-shadow 0.3s; /* Smooth transitions for interactions */ } /* Focus state for input fields */ .form-group input:focus {   border-color: #4a90e2; /* Blue border on focus */   box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.25); /* Subtle glow effect */   outline: none; /* Remove default browser outline */ } /* Password field wrapper for positioning the toggle button */ .password-field {   position: relative; /* Allows absolute positioning of child elements */   display: flex;   align-items: center; } /* Password visibility toggle button */ .toggle-password {   position: absolute;   right: 10px; /* Position from the right edge */   top: 50%;   transform: translateY(-50%); /* Center vertically */   background: none;   border: none;   cursor: pointer;   padding: 0;   color: #777; } /* Eye icon styling */ .eye-icon {   width: 20px;   height: 20px;   fill: #777; /* Gray color for the icon */ } /* Toggle between eye icons when password visibility changes */ .password-visible .eye-open {   display: none; /* Hide open eye when password is visible */ } .password-visible .eye-closed {   display: block !important; /* Show closed eye when password is visible */ } /* Error state styling for form inputs */ .form-group input.error {   border-color: #e74c3c; /* Red border for validation errors */ } /* Error message styling */ .error-message {   color: #e74c3c; /* Red text for error messages */   font-size: 0.8rem;   margin-top: 0.5rem;   display: none; /* Hidden by default, shown via JavaScript */ } /* Form options container (Remember me and Forgot password) */ .form-options {   display: flex;   justify-content: space-between; /* Space items evenly */   align-items: center;   margin-bottom: 1.5rem; } /* Remember me checkbox styling */ .remember-me {   display: flex;   align-items: center; } .remember-me input[type=”checkbox”] {   margin-right: 0.5rem; /* Space between checkbox and label */ } .remember-me label {   font-size: 0.9rem;   color: #666; } /* Forgot password link styling */ .forgot-password {   font-size: 0.9rem;   color: #4a90e2; /* Blue color for links */   text-decoration: none; } .forgot-password:hover {   text-decoration: underline; /* Underline on hover for better UX */ } /* Main login button styling */ .login-button {   width: 100%;   padding: 0.8rem;   background: #4a90e2; /* Primary blue background */   border: none;   border-radius: 4px;   color: white;   font-size: 1rem;   font-weight: 500;   cursor: pointer;   transition: background 0.3s, transform 0.1s; /* Smooth hover and click effects */ } /* Button hover state */ .login-button:hover {   background: #3a80d2; /* Darker blue on hover */ } /* Button active/pressed state */ .login-button:active {   transform: scale(0.98); /* Slight scale down when clicked */ } /* Registration link container */ .register-link {   text-align: center;   margin-top: 1.5rem;   font-size: 0.9rem;   color: #666; } /* Registration link styling */ .register-link a {   color: #4a90e2; /* Blue color to match other links */   text-decoration: none; } .register-link a:hover {   text-decoration: underline; /* Underline on hover */ }

I’ve used several modern CSS techniques in this design:

  • Flexbox for centering the form and aligning elements
  • Subtle gradient background to create visual interest
  • Box shadows to create depth
  • Transition effects for interactive elements
  • Clean typography with careful spacing and hierarchy


Figure 2. Complete styled login page with gradient background and modern UI

Responsive Design Tips

Let’s just add some responsive styles with a media query to ensure our login page works well across all devices:

/* Responsive design for mobile devices */ @media (max-width: 480px) {   .login-container {       padding: 1.5rem; /* Reduced padding on small screens */   }   .login-header h1 {       font-size: 2rem; /* Smaller heading on mobile */   }   /* Stack form options vertically on small screens */   .form-options {       flex-direction: column;       align-items: flex-start;   }   .forgot-password {       margin-top: 0.5rem; /* Add space when stacked */   } }

With these rules, we ensure that on smaller screens:

  • The container uses less padding so that we maximize available space
  • The header text is slightly smaller
  • The “Remember me” and “Forgot password” options stack vertically
  • All elements remain comfortably sized for touch input

Adding Client-Side Validation

Now that everything is visually fine, we can add some JavaScript to validate the users’ input and animate the password visibility toggle (the eye icon).

document.addEventListener(‘DOMContentLoaded’, function() {     const form = document.querySelector(‘.login-form’);     const passwordInput = document.getElementById(‘password’);     const togglePassword = document.querySelector(‘.toggle-password’);     const errorMessage = document.querySelector(‘.error-message’);     const eyeOpen = document.querySelector(‘.eye-open’);     const eyeClosed = document.querySelector(‘.eye-closed’);     // Toggle password visibility     togglePassword.addEventListener(‘click’, function() {         // Toggle password field type         const type = passwordInput.getAttribute(‘type’) === ‘password’ ? ‘text’ : ‘password’;         passwordInput.setAttribute(‘type’, type);         // Toggle eye icon         if (type === ‘text’) {             eyeOpen.style.display = ‘none’;             eyeClosed.style.display = ‘block’;         } else {             eyeOpen.style.display = ‘block’;             eyeClosed.style.display = ‘none’;         }     });     // Form validation     form.addEventListener(‘submit’, function(e) {         let valid = true;         // Reset previous error states         errorMessage.style.display = ‘none’;         passwordInput.classList.remove(‘error’);         // Validate password         if (passwordInput.value.length < 6) {             valid = false;             passwordInput.classList.add(‘error’);             errorMessage.style.display = ‘block’;         }         if (!valid) {             e.preventDefault();         }     }); });

With this short script, we have a fully functional form that looks professional and polished.


Figure 3. Form validation in action with error message and eye icon toggle

Security Considerations

Security should never be compromised, so we should keep in mind these best practices:

  1. Always use HTTPS for login pages to encrypt data transmission
  2. Implement CSRF protection by adding a hidden token to your form
  3. Sanitize all inputs on the server side to prevent injection attacks
  4. Use appropriate autocomplete attributes to help password managers work correctly

<input type=“text” id=“username” name=“username” autocomplete=“username” required>

<input type=“password” id=“password” name=“password” autocomplete=“current-password” required>

  1. Implement proper password policies that balance security with usability
  2. Provide clear feedback for authentication errors without revealing too much information

My Design Story

One of my favorite login pages that I built was back when I used to do freelancing projects. The client wanted a design that was both professional and approachable, while being highly usable for users of all ages.

I wanted to go above and beyond, so I made several deliberate design choices similar to what you can see in the form we’ve built above:

  1. Used a soft blue gradient background to create a calm, trustworthy feeling
  2. Designed clear, direct error messages that appear below the field rather than in popups
  3. Implemented the password visibility toggle to reduce frustration for users
  4. Created subtle animations on the button and form inputs for a more responsive feel
  5. Used ample white space and clear typography for maximum readability

The feedback was overwhelmingly positive, with the client specifically commenting on how easy the login process was compared to competitor sites they used. If you take away something from this article, please let it be that everything, even “simple” login forms, deserve thoughtful design attention.

Wrapping Up

Building a modern login page with HTML, CSS, and JS is a balance of aesthetics, functionality, and security. I’ve tried to address all of this with:

  • A clean, professional appearance with a calming color scheme
  • Well-structured HTML for accessibility and maintainability
  • Modern CSS techniques for visual appeal
  • Responsive design for all devices
  • Client-side validation for improved user experience
  • Security considerations integrated from the beginning

You should always keep in mind that a login page is often your users’ first interaction with your application. By following these techniques, you can create a welcoming, trustworthy entry point that sets the tone for the entire user experience.If you want to take your web development skills further, Udacity offers several programs that can help. The Front End Web Developer Nanodegree teaches you how to create engaging user interfaces with HTML, CSS, and JavaScript. If you are interested in full-stack development, the Full Stack Web Developer Nanodegree program covers both front-end and back-end technologies including authentication systems. And if you’re just getting started, the Intro to Programming Nanodegree program provides a solid foundation in HTML and CSS fundamentals.

Alan Sánchez Pérez Peña
Alan Sánchez Pérez Peña
Alan is a seasoned developer and a Digital Marketing expert, with over a decade of software development experience. He has executed over 70,000+ project reviews at Udacity, and his contributions to course and project development have significantly enhanced the learning platform. Additionally, he provides strategic web consulting services, leveraging his front-end expertise with HTML, CSS, and JavaScript, alongside his Python skills to assist individuals and small businesses in optimizing their digital strategies. Connect with him on LinkedIn here: http://www.linkedin.com/in/alan247