HTML Formatterhtmlformatteronline.com

Login form

A sign-in card with a show-password toggle and a single generic error.

formloginauthpasswordsignin
<form class="auth" id="login" novalidate>
  <div class="mark">A</div>
  <h1>Sign in to Acme</h1>
  <p class="sub">New here? <a href="/signup/">Create an account</a></p>

  <div class="field">
    <label for="email">Email address</label>
    <input type="email" id="email" name="email" autocomplete="username"
           required autofocus aria-describedby="form-err">
  </div>

  <div class="field">
    <div class="row">
      <label for="password">Password</label>
      <a class="small" href="/reset/">Forgot?</a>
    </div>
    <div class="pw">
      <input type="password" id="password" name="password"
             autocomplete="current-password" required aria-describedby="form-err">
      <button type="button" class="peek" id="peek" aria-pressed="false" aria-label="Show password">
        <svg viewBox="0 0 24 24" aria-hidden="true">
          <path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z"/>
          <circle cx="12" cy="12" r="3"/>
        </svg>
      </button>
    </div>
  </div>

  <p class="err" id="form-err" hidden>Email or password is incorrect.</p>

  <label class="check">
    <input type="checkbox" name="remember" checked>
    <span>Keep me signed in</span>
  </label>

  <button class="submit" type="submit">Sign in</button>

  <div class="divider"><span>or</span></div>

  <button class="oauth" type="button">
    <svg viewBox="0 0 24 24" aria-hidden="true" class="g"><path d="M21.8 12.2c0-.7-.1-1.4-.2-2H12v3.9h5.5a4.7 4.7 0 0 1-2 3.1v2.6h3.2c1.9-1.7 3-4.3 3-7.6z"/><path d="M12 22c2.7 0 5-.9 6.7-2.4l-3.2-2.6c-.9.6-2 1-3.5 1-2.7 0-5-1.8-5.8-4.3H2.9v2.7A10 10 0 0 0 12 22z"/><path d="M6.2 13.7a6 6 0 0 1 0-3.8V7.2H2.9a10 10 0 0 0 0 9.2l3.3-2.7z"/><path d="M12 5.9c1.5 0 2.9.5 3.9 1.5l2.9-2.9A10 10 0 0 0 2.9 7.2l3.3 2.7C7 7.7 9.3 5.9 12 5.9z"/></svg>
    Continue with Google
  </button>
</form>

How it works

autocomplete="username" on the email field. This is the one people get wrong, and it is what password managers look for. Paired with autocomplete="current-password", browsers and managers fill both fields reliably. On a signup form the password field should be new-password instead, which prompts managers to suggest a generated one.

One generic error, never two specific ones. "Email or password is incorrect" rather than "No account with that email". A specific message confirms which addresses are registered, which hands an attacker a way to enumerate your users.

The show-password toggle uses aria-pressed. It is a toggle button, so the state belongs in aria-pressed and the label changes with it. Focus returns to the field afterwards so typing continues where it left off.

Do not block paste. Nothing here interferes with pasting into the password field. Blocking paste is still common and it actively harms security by making password managers unusable, which pushes people back to passwords they can remember.

The button reports its own state while the request is in flight, and is disabled to prevent a double submit.

Accessibility notes

autofocus on the email field is appropriate here because signing in is unambiguously the only purpose of the page. On a page with other content it is disorienting and should be left off.

The error message sits above the fields and is linked to both inputs with aria-describedby, so it is announced with either field rather than being missed entirely.

The Google icon is aria-hidden because the button text already says what it does.

Making it yours

Add a rate limit and an account lockout on the server. Nothing on the client side is a security control — everything here is about the experience of the person who does know their password.

For a signup form, change current-password to new-password, add a strength meter that measures actual entropy rather than counting character classes, and drop any maximum length below 64 characters.

Related templates

Check your version

Once you have edited this, the HTML validator will catch any tag you left unclosed, and the formatter will tidy the indentation. Both run in your browser.