CSS Tutorial: Modern Login Form

In this tutorial I'll show you how I went about designing a login form, including my reasoning for various design choices and the CSS implementations behind them.

We'll start with a simple HTML template and gradually add styles to it.

Here's a Codepen link for the final design: Login form component.

This is the HTML and CSS we will start with:

<!DOCTYPE html>
<html>
  <head>
    <title>Sign in form</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
      html {
        font-size: 125%;
        line-height: 1.5;
        box-sizing: border-box;
        color: rgb(22, 22, 22);
      }

      *,
      *:before,
      *:after {
        box-sizing: inherit;
      }

      body {
        margin: 0;
      }

      h1 {
        margin: 0;
        padding: 0;
        margin-bottom: 1em;
      }

      a {
        color: #828282;
      }

      a:hover {
        color: #a8a8a8;
      }
    </style>
  </head>
  <body>
    <div class="login-form">
      <h1>Sign in</h1>
      <form>
        <div>
          <label for="username">Username</label>
          <input type="text" placeholder="Enter your username" name="username" id="username" />
        </div>
        <div>
          <label for="password">Password</label>
          <input type="password" placeholder="Enter your password" name="password" id="password" />
        </div>
        <button>Sign in</button>
      </form>

      <div class="forgotten-password">
        <a href='#'>Forgotten your password?</a>
      </div>

      <div class="sign-up">
        <a href='#'>New user? Create an account</a>
      </div>
    </div>
  </body>
</html>

Here's a Codepen for the above code for you to start out with: Login form component base HTML & CSS.

As I add new styles, you can add the changes to the Codepen.

Anyway, a few notes about this initial code:

  • The page's font size is set to a percentage value rather than a px value. This is a good practise to adopt because it scales with user-defined browser settings. The default font size for most browsers is 16px, so by default our page will have a font size of 20px (125% of 16 is 20).
  • line-height is set to 1.5 on all elements. This means line heights will automatically scale with font sizes. For example, if an element's font size is 30px and it has a line height of 1.5, the final line height pixel value the browser computes will be 45px.
  • I've set box-sizing: border-box on every element on the page. This is another good practise you should adopt. This property stops elements being resized when you add borders or padding to them, keeping element widths and heights consistent. For more information see this post: Box-sizing: Border-box FTW

Font Styling

Before designing the form I'll add some styling to the text first. I'll use a font called Montserrat, which Google Fonts provides for free.

Place this <link> tag in the document's <head> tag to load Montserrat from Google fonts:

<link href="https://fonts.googleapis.com/css?family=Montserrat:400,400i,700|Josefin+Sans:400,400i,700" rel="stylesheet" />

Then add this to the page's CSS:

html,
input,
button {
  font-family: "Montserrat", "Helvetica", sans-serif;
}

Note that I'm setting the font-family not just on html elements, but also on input and button elements. This is because form elements don't inherit the page's font and you must specifically declare what font these elements should use.

You can specify a list of fonts for font-family. If the browser can't load the first font, it will try to load the second one, and so on until a usable font is found.

Thus the text on the page will use Montserrat. But if the browser can't load that font for whatever reason, Helvetica will be used. Most systems come with Helvetica pre-installed so it's a pretty safe bet against your site defaulting to Times New Roman.

Margin and padding

Next let's space the elements out by setting some margin and padding values. Here's the CSS for that:

.login-form {
  padding: 2em;
}

.login-form label {
  display: block;
  margin-bottom: 0.25em;
}

.login-form input {
  margin-bottom: 1em;
}

.login-form button {
  margin-bottom: 1em;
}

.forgotten-password {
  margin-bottom: 1em;
}

The container for the login form (.login-form) gets a fair amount of padding. This puts some white space around the form, making it look more pleasing than if its content was packed right at the sides of the element.

The elements inside the form are separated by setting some bottom margin on each one.

I'm using em for setting margin and padding values. This means the margin and padding values for each element scale according to the element's font size, rather than having fixed values.

If you're unfamiliar with em (or rem) measurements then read this post: REM vs EM.

Form field styling

Default form fields look pretty bad so let's add some styling to them:

.login-form label {
  display: block;
  margin-bottom: 0.25em;
  font-weight: bold;
}

.login-form input {
  width: 100%;
  font-size: 1rem;
  padding: 0.75em;
  border: 0;
  border-bottom: 3px solid #ebeff0;
  border-radius: 3px;
  background: #f4f9fb;
  margin-bottom: 1em;
}

All I've added to the label styling is font-weight: bold to make the label text stand out.

For the input fields, I've made quite a few changes so I'll go through each one.

I've set the font-size property on .login-form input because form fields don't inherit font styles from parent elements, and the default size in the browser I'm using, Chrome, is way too small.

I've set the width on .login-form input to 100% to make the input elements span across the screen. This looks good on mobile. This is unsuitable for desktop, but we'll sort that out later (it's typical nowadays to design a page "mobile-first", that is you style the page by default for mobile devices, then add further changes for desktop).

I've removed the border from the input fields because the default one is just ugly.

I've set the background colour on the input fields to #f4f9fb, and also given them a slight border radius, to give them a fuller look.

And finally I've set a 3px border at the bottom of each input element to a colour that's a bit darker than the background colour, #ebeff0. This is a technique I've seen used in many login forms, and I just think it looks pretty nifty.

Button styling

I'm going to apply a similar set of changes to the form's button element, though with some differences. Here's the updated .login-form button CSS:

.login-form button {
  width: 100%;
  font-size: 1em;
  font-weight: bold;
  padding: 0.75em;
  background: #4facfe;
  color: #fff;
  border: 0;
  border-radius: 3px;
  margin-bottom: 1em;
  cursor: pointer;
}

The main difference between the button and the input elements is that the button stands out on the page, thanks to its background colour and font style.

The sign in button is a call to action, and you always want them to stand out, similar to how you'd want a "Buy now" button to stand out. It should always be stupidly obvious where CTA buttons are on your page.

Forgotten password / sign up links

I'm going to add some styling to make the forgotten password and sign up links look nicer:

.forgotten-password {
  text-align: center;
  margin-bottom: 1rem;
  font-size: 0.75em;
}

.sign-up {
  border-top: 1px solid rgb(224, 224, 224);
  padding-top: 1em;
  text-align: center;
  font-size: 0.75em;
}

This spaces the links out a bit and adds a light horitontal divide between them.

Desktop

The form's looking good mobile but it's pretty naff on desktop.

I'll use a media query to add a few styles for devices with a screen width of at least 720px. This is a pretty simple and arbitrary cut-off point but it'll suffice for this tutorial.

@media screen and (min-width: 720px) {
  .login-form {
    border-radius: 6px;
    margin: 0 auto;
    max-width: 25em;
  }

  .login-form {
    margin-top: 5em;
  }
}

And then I'll add a background to the page and the login form:

body {
  background-image: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
  margin: 0;
}

.login-form {
  background-color: #fff;
  padding: 2em;
}

On desktop, the login form now appears around the center of the page, surrounded by a gradient background.

The design of a white container element against a vibrant gradient background is a design that quite a lot of people use, and can look very professional.

But making a nice looking CSS gradient can be tricky. You can always nab some from webgradients.com.

Final touches

These are just a few extra bits of styling to polish things a bit.

First I think the default colour of the placeholder text in the input fields is too sharp. I'd rather the label text stood out more. Fortunately you can change this:

.login-form input::placeholder {
  color: #a7a7a7;
}

The placeholder text colour is now a little dimmer, allowing the labels to stand out more.

Next you might notice that if you focus on an input element, an ugly blue border appears around it. You can get rid of this default browser behaviour by setting outline: none on an element:

.login-form input:focus {
  outline: none;
}

Finally, it can look good to add a subtle box shadow around an element. I'll stick one on the main .login-form element. Here's the final CSS for .login-form:

.login-form {
  background-color: #fff;
  padding: 2em;
  box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
}

End product

We've now created a clean, modern design for a sign in form. Feel free to have a go at improving it.

Maybe you have some ideas on how it could look way better.

Or perhaps you'll want to implement a new/missing feature on the form. For example, error highlighting on the form fields, or a 'remember me' option.

Either way let me know what enhancements you make.