Auth Forms
Reset password with strength meter
New password form with live requirements
New password form with live requirements
Create a reset-password card with Tailwind CSS. Include a title, a line naming the account being reset, a new password field, a four-segment strength meter bar that starts empty, a small strength label, a confirm field, and a requirements checklist whose items each carry a data-rule attribute. Finish with a full-width dark submit button and a back-to-sign-in link. Then add JavaScript that scores the password on every keystroke — length, a number, a symbol, mixed case — paints that many meter segments amber then green, writes Weak / Fair / Good / Strong into the label, flips each checklist row between a muted dot and a green check as its rule passes, and keeps the submit button disabled until every rule passes including the two fields matching. <div class="flex min-h-screen items-center justify-center bg-neutral-100 p-6">
<div class="w-full max-w-md rounded-2xl border border-neutral-200 bg-white p-8 shadow-sm">
<h1 class="text-xl font-semibold text-neutral-900">Choose a new password</h1>
<p class="mt-1 text-sm text-neutral-500">Resetting the password for <span class="font-medium text-neutral-700">[email protected]</span></p>
<form class="mt-6 space-y-4">
<input id="pw" type="password" placeholder="New password"
class="w-full rounded-xl border border-neutral-200 px-3.5 py-2.5 text-sm outline-none focus:border-neutral-900 focus:ring-4 focus:ring-neutral-900/10" />
<div>
<div id="meter" class="flex gap-1.5">
<span class="h-1.5 flex-1 rounded-full bg-neutral-200"></span>
<span class="h-1.5 flex-1 rounded-full bg-neutral-200"></span>
<span class="h-1.5 flex-1 rounded-full bg-neutral-200"></span>
<span class="h-1.5 flex-1 rounded-full bg-neutral-200"></span>
</div>
<p class="mt-1.5 text-xs text-neutral-500">Strength: <span id="strength" class="font-medium text-neutral-900">—</span></p>
</div>
<input id="confirm" type="password" placeholder="Confirm new password"
class="w-full rounded-xl border border-neutral-200 px-3.5 py-2.5 text-sm outline-none focus:border-neutral-900 focus:ring-4 focus:ring-neutral-900/10" />
<ul class="space-y-1.5 text-xs">
<li data-rule="length" class="flex items-center gap-2 text-neutral-400"><span data-icon>•</span> At least 12 characters</li>
<li data-rule="number" class="flex items-center gap-2 text-neutral-400"><span data-icon>•</span> Contains a number</li>
<li data-rule="symbol" class="flex items-center gap-2 text-neutral-400"><span data-icon>•</span> Contains a symbol</li>
<li data-rule="match" class="flex items-center gap-2 text-neutral-400"><span data-icon>•</span> Both passwords match</li>
</ul>
<button id="submit" disabled class="w-full cursor-not-allowed rounded-xl bg-neutral-900 py-2.5 text-sm font-medium text-white opacity-40 transition hover:bg-neutral-800">Update password</button>
</form>
<p class="mt-6 text-center text-sm text-neutral-500">
<a href="#" class="font-medium text-neutral-900 hover:underline">← Back to sign in</a>
</p>
</div>
</div> var pw = document.getElementById("pw");
var confirm = document.getElementById("confirm");
var segments = [].slice.call(document.querySelectorAll("#meter span"));
var strength = document.getElementById("strength");
var submit = document.getElementById("submit");
var rules = [].slice.call(document.querySelectorAll("[data-rule]"));
var LABELS = ["—", "Weak", "Fair", "Good", "Strong"];
function check(value, other) {
return {
length: value.length >= 12,
number: /[0-9]/.test(value),
symbol: /[^A-Za-z0-9]/.test(value),
match: value.length > 0 && value === other
};
}
function render() {
var value = pw.value;
var passed = check(value, confirm.value);
// Score the password itself; "match" is a gate on submit, not a strength signal.
var score = 0;
if (passed.length) score += 1;
if (passed.number) score += 1;
if (passed.symbol) score += 1;
if (/[a-z]/.test(value) && /[A-Z]/.test(value)) score += 1;
if (!value) score = 0;
segments.forEach(function (seg, i) {
var filled = i < score;
seg.className =
"h-1.5 flex-1 rounded-full " +
(!filled ? "bg-neutral-200" : score >= 4 ? "bg-green-500" : score >= 2 ? "bg-amber-500" : "bg-red-500");
});
strength.textContent = LABELS[score];
rules.forEach(function (row) {
var ok = passed[row.getAttribute("data-rule")];
row.className = "flex items-center gap-2 " + (ok ? "text-green-600" : "text-neutral-400");
row.querySelector("[data-icon]").textContent = ok ? "✓" : "•";
});
var ready = passed.length && passed.number && passed.symbol && passed.match;
submit.disabled = !ready;
submit.classList.toggle("opacity-40", !ready);
submit.classList.toggle("cursor-not-allowed", !ready);
}
pw.addEventListener("input", render);
confirm.addEventListener("input", render);
render();