Component Library

← Back to gallery

Forgot password form

Category: Authentication · Tags: password-reset, forgot-password, account-recovery, form

Reset your password

Enter the email you signed up with and we will send you a link to choose a new password.

Back to sign in

How to use it

Use on a password-reset request page. Keep the confirmation neutral, as here, so the page never tells an attacker which emails have accounts; do not replace it with "No account found". The submit handler only prevents submission and shows the demo confirmation: the developer must wire the real reset endpoint, point Back to sign in at the real sign-in page, and rename the dcl-forgot ids if it appears twice.

Accessibility

A native <form> labelled by its heading, with a <label for> on the email field (autocomplete="email") and a short instruction tied to it with aria-describedby. After submission the confirmation is written into a role="status" region so screen readers announce it without moving focus, and the same wording is used whether or not the account exists. Text #0f172a on white is 17.85:1, the instruction #475569 on white is 7.58:1, the confirmation #166534 on #f0fdf4 is 6.81:1, the link #1d4ed8 on white is 6.70:1, and the button's white on #1d4ed8 (#1e40af hovered) is 6.70:1 (8.72:1).

Source (copy-ready)

<form class="dcl-forgot" aria-labelledby="dcl-forgot-title">
  <h2 id="dcl-forgot-title" class="dcl-forgot__title">Reset your password</h2>
  <p id="dcl-forgot-help" class="dcl-forgot__help">Enter the email you signed up with and we will send you a link to choose a new password.</p>
  <label for="dcl-forgot-email" class="dcl-forgot__label">Email address</label>
  <input type="email" id="dcl-forgot-email" name="email" class="dcl-forgot__input" autocomplete="email" required aria-describedby="dcl-forgot-help">
  <button type="submit" class="dcl-forgot__submit">Send reset link</button>
  <p class="dcl-forgot__status" role="status"></p>
  <a href="../index.html" class="dcl-forgot__link">Back to sign in</a>
</form>
<style>
.dcl-forgot { display: flex; flex-direction: column; gap: 0.75rem; max-width: 22rem; padding: 1.5rem; border: 1px solid #cbd5e1; border-radius: 0.75rem; font: inherit; color: #0f172a; background-color: #ffffff; box-sizing: border-box; }
.dcl-forgot__title { margin: 0; font-size: 1.375rem; color: #0f172a; background-color: #ffffff; }
.dcl-forgot__help { margin: 0; color: #475569; background-color: #ffffff; }
.dcl-forgot__label { font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-forgot__input { width: 100%; box-sizing: border-box; padding: 0.55rem 0.65rem; border: 1px solid #64748b; border-radius: 6px; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-forgot__input:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-forgot__submit { padding: 0.65rem 1rem; border: none; border-radius: 6px; font: inherit; font-weight: 600; cursor: pointer; color: #ffffff; background-color: #1d4ed8; }
.dcl-forgot__submit:hover { color: #ffffff; background-color: #1e40af; }
.dcl-forgot__submit:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-forgot__status { margin: 0; color: #166534; background-color: #ffffff; }
.dcl-forgot__status.dcl-forgot__status--sent { padding: 0.65rem 0.75rem; border-left: 4px solid #15803d; border-radius: 4px; color: #166534; background-color: #f0fdf4; }
.dcl-forgot__link { align-self: flex-start; color: #1d4ed8; background-color: #ffffff; }
.dcl-forgot__link:hover { color: #1e40af; background-color: #ffffff; }
.dcl-forgot__link:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
</style>
<script>
(function () {
  document.querySelectorAll(".dcl-forgot").forEach(function (form) {
    if (form.dataset.dclBound) {
      return;
    }
    form.dataset.dclBound = "true";
    form.addEventListener("submit", function (event) {
      event.preventDefault();
      var status = form.querySelector(".dcl-forgot__status");
      if (status) {
        status.classList.add("dcl-forgot__status--sent");
        status.textContent = "If an account exists for that email, a reset link is on its way. It expires in 30 minutes. (Demo only: nothing was sent.)";
      }
    });
  });
})();
</script>

Was this useful? Send feedback.