Authentication Forms
Sign-in, registration, password-recovery and server-validation examples composed from forms, inputs, checkboxes, buttons, alerts and utility classes.
Sign in
Use a form-level alert for a credential error when no single field caused the failure. A generic message avoids revealing whether an account exists.
<form class="card shadow-sm" action="/sessions" method="post" aria-labelledby="authentication-sign-in-title">
<h3 id="authentication-sign-in-title">Sign in</h3>
<div class="alert bg-error" role="alert">
<i class="icon icon-md alert-icon" aria-hidden="true"><!-- error icon --></i>
<div class="alert-content">
<p class="alert-title">We could not sign you in</p>
<p class="alert-message">Check your email and password, then try again.</p>
</div>
</div>
<div>
<label class="form-label is-required" for="signInEmail">Email address</label>
<input
class="form-control"
id="signInEmail"
name="email"
type="email"
autocomplete="username"
required />
</div>
<div>
<label class="form-label is-required" for="signInPassword">Password</label>
<input
class="form-control"
id="signInPassword"
name="password"
type="password"
autocomplete="current-password"
required />
<a href="#authentication-password-recovery-title">Forgot your password?</a>
</div>
<div class="form-check">
<input
class="form-check-input"
id="rememberSignIn"
name="remember"
type="checkbox"
value="yes" />
<label class="form-label" for="rememberSignIn">Keep me signed in</label>
</div>
<button class="btn btn-solid btn-primary w-100" type="submit">Sign in</button>
</form>Password recovery
Ask only for the account identifier needed to start recovery. Return the same generic response whether the account exists so the form does not expose registered email addresses.
<form class="card shadow-sm" action="/password-recovery" method="post" aria-labelledby="authentication-password-recovery-title">
<header>
<h3 id="authentication-password-recovery-title">Reset your password</h3>
<p>Request a secure reset link</p>
</header>
<div class="border border-primary bg-primary bg-opacity-10 rounded p-3 d-flex align-items-start gap-2">
<i class="icon icon-md text-primary" aria-hidden="true"><!-- information icon --></i>
<div>
<p class="h6 mb-1">Check your inbox</p>
<p class="mb-0">
If an account matches the address, we will send password-reset instructions.
</p>
</div>
</div>
<div>
<label class="form-label is-required" for="recoveryEmail">Email address</label>
<input
class="form-control"
id="recoveryEmail"
name="email"
type="email"
autocomplete="username"
required />
</div>
<div class="d-flex flex-column flex-sm-row gap-2">
<a class="btn btn-text btn-secondary" href="/sign-in">Back to sign in</a>
<button class="btn btn-solid btn-primary" type="submit">Send reset link</button>
</div>
</form>Registration with server validation
On an unsuccessful server response, move focus to the error summary and link each message to its field. Preserve safe text and choice values so users only correct the errors; clear password fields instead of returning secrets in HTML.
<form class="card shadow-sm" action="/accounts" method="post" aria-labelledby="authentication-server-validation-title">
<header>
<h3 id="authentication-server-validation-title">Create an account</h3>
<p>Correct the highlighted fields</p>
</header>
<div
class="alert bg-error"
role="alert"
aria-labelledby="registrationErrorSummaryTitle"
autofocus
tabindex="-1">
<i class="icon icon-md alert-icon" aria-hidden="true"><!-- error icon --></i>
<div class="alert-content">
<h4 class="alert-title h6" id="registrationErrorSummaryTitle">
There are two errors to correct
</h4>
<ul class="mb-0">
<li><a class="text-inherit" href="#serverRegistrationEmail">Use a different email address</a></li>
<li><a class="text-inherit" href="#serverRegistrationPassword">Choose a password that has not appeared in a known data breach</a></li>
</ul>
</div>
</div>
<div>
<label class="form-label is-required" for="serverRegistrationName">Full name</label>
<input
class="form-control"
id="serverRegistrationName"
name="name"
type="text"
value="John Doe"
autocomplete="name"
required />
</div>
<div>
<label class="form-label is-required" for="serverRegistrationEmail">Email address</label>
<input
class="form-control is-invalid"
id="serverRegistrationEmail"
name="email"
type="email"
value="john.doe@example.com"
autocomplete="email"
aria-invalid="true"
aria-describedby="serverRegistrationEmailFeedback"
required />
<div class="invalid-feedback" id="serverRegistrationEmailFeedback">
An account already uses this email address.
</div>
</div>
<div>
<label class="form-label is-required" for="serverRegistrationPassword">Password</label>
<input
class="form-control is-invalid"
id="serverRegistrationPassword"
name="password"
type="password"
autocomplete="new-password"
minlength="12"
aria-invalid="true"
aria-describedby="serverRegistrationPasswordHint serverRegistrationPasswordFeedback"
required />
<small class="text-muted" id="serverRegistrationPasswordHint">Use at least 12 characters.</small>
<div class="invalid-feedback" id="serverRegistrationPasswordFeedback">
Choose a password that has not appeared in a known data breach.
</div>
</div>
<div>
<label class="form-label is-required" for="serverRegistrationPasswordConfirmation">
Confirm password
</label>
<input
class="form-control"
id="serverRegistrationPasswordConfirmation"
name="passwordConfirmation"
type="password"
autocomplete="new-password"
required />
</div>
<div class="form-check">
<input
class="form-check-input"
id="serverAcceptTerms"
name="terms"
type="checkbox"
value="accepted"
checked
required />
<label class="form-label is-required" for="serverAcceptTerms">
I accept the terms and data policy
</label>
</div>
<button class="btn btn-solid btn-primary w-100" type="submit">Create account</button>
</form>Registration
Pair every required field with its visible label, and expose password guidance through aria-describedby. Give required checkboxes an explicit submission value.
<form class="card shadow-sm" action="/accounts" method="post" aria-labelledby="authentication-registration-title">
<h3 id="authentication-registration-title">Create an account</h3>
<div class="border border-primary bg-primary bg-opacity-10 rounded p-3 d-flex align-items-start gap-2">
<i class="icon icon-md text-primary" aria-hidden="true"><!-- information icon --></i>
<div>
<p class="h6 mb-1">Choose a strong password</p>
<p class="mb-0">Password managers can create and store one for you.</p>
</div>
</div>
<div>
<label class="form-label is-required" for="registrationName">Full name</label>
<input
class="form-control"
id="registrationName"
name="name"
autocomplete="name"
required />
</div>
<div>
<label class="form-label is-required" for="registrationEmail">Email address</label>
<input
class="form-control"
id="registrationEmail"
name="email"
type="email"
autocomplete="email"
required />
</div>
<div>
<label class="form-label is-required" for="registrationPassword">Password</label>
<input
class="form-control"
id="registrationPassword"
name="password"
type="password"
autocomplete="new-password"
minlength="12"
aria-describedby="registrationPasswordHint"
required />
<small class="text-muted" id="registrationPasswordHint">Use at least 12 characters.</small>
</div>
<div>
<label class="form-label is-required" for="registrationPasswordConfirmation">
Confirm password
</label>
<input
class="form-control"
id="registrationPasswordConfirmation"
name="passwordConfirmation"
type="password"
autocomplete="new-password"
required />
</div>
<div class="form-check">
<input
class="form-check-input"
id="acceptTerms"
name="terms"
type="checkbox"
value="accepted"
required />
<label class="form-label is-required" for="acceptTerms">
I accept the terms and data policy
</label>
</div>
<button class="btn btn-solid btn-primary w-100" type="submit">Create account</button>
</form>