forms otp verification auth input 2fa otp verification code input auth 2fa sms authenticator OTP verification code input one-time password digit boxes
OTP Input
Fetch pattern JSON:
curl https://webspire.de/patterns/forms/otp-input.json otp-input.html
<section class="ws-forms-otp min-h-screen bg-[var(--ws-color-bg)] py-20">
<div class="mx-auto flex max-w-sm flex-col gap-10 px-6">
<!-- Variant 1: 6-digit OTP -->
<div class="rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] p-8 text-center">
<div class="mb-2 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--ws-color-bg-secondary)] mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-[var(--ws-color-accent)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<h2 class="mt-4 text-xl font-bold text-[var(--ws-color-text)]">Enter verification code</h2>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)]">We sent a 6-digit code to <span class="font-medium text-[var(--ws-color-text)]">hello@example.com</span></p>
<div class="mt-6 flex justify-center gap-2">
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 1" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 2" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 3" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 4" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 5" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 6" class="otp-box h-14 w-12 rounded-xl border border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
</div>
<p class="mt-5 text-sm text-[var(--ws-color-text-soft)]">
Didn't receive a code?
<button type="button" class="font-medium text-[var(--ws-color-accent)] hover:underline">Resend in 0:48</button>
</p>
<button type="button" class="mt-5 w-full rounded-xl bg-[var(--ws-color-accent)] px-6 py-3 text-sm font-semibold text-white transition hover:opacity-90">
Verify code
</button>
</div>
<!-- Variant 2: 4-digit OTP, larger boxes -->
<div class="rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] p-8 text-center">
<h2 class="text-xl font-bold text-[var(--ws-color-text)]">Confirm your PIN</h2>
<p class="mt-2 text-sm text-[var(--ws-color-text-soft)]">Enter the 4-digit code from your authenticator app.</p>
<div class="mt-6 flex justify-center gap-3">
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 1" class="otp-box h-20 w-16 rounded-2xl border-2 border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-2xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 2" class="otp-box h-20 w-16 rounded-2xl border-2 border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-2xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 3" class="otp-box h-20 w-16 rounded-2xl border-2 border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-2xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
<input type="text" inputmode="numeric" maxlength="1" aria-label="Digit 4" class="otp-box h-20 w-16 rounded-2xl border-2 border-[var(--ws-color-border)] bg-[var(--ws-color-bg)] text-center text-2xl font-bold text-[var(--ws-color-text)] transition focus:border-[var(--ws-color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--ws-color-accent)]/30" />
</div>
<p class="mt-5 text-sm text-[var(--ws-color-text-soft)]">
Lost access?
<a href="#" class="font-medium text-[var(--ws-color-accent)] hover:underline">Use a recovery code</a>
</p>
<button type="button" class="mt-5 w-full rounded-xl bg-[var(--ws-color-accent)] px-6 py-3 text-sm font-semibold text-white transition hover:opacity-90">
Confirm
</button>
</div>
</div>
<style>
/* OTP boxes: strong focus ring, filled state highlight */
.otp-box:focus {
border-color: var(--ws-color-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ws-color-accent) 20%, transparent);
}
/* Auto-advance requires JS — add an input event listener that calls nextSibling.focus() */
</style>
</section>
Details
Responsive Dark Mode Tailwind Only SSR Safe Copy & Paste
Stable Published
formsotpverificationauthinput2fa
Slots
| Name | Required | Description |
|---|---|---|
| otp-6 | Yes | 6-digit OTP entry with resend link. |
| otp-4 | Yes | 4-digit PIN entry with larger boxes. |
Two stacked OTP cards. The first shows a 6-digit code entry with icon, subtitle, resend countdown, and verify button. The second shows a larger 4-digit PIN variant. Focus ring and border accent are applied via CSS — auto-advance between boxes requires a small JS snippet (see install notes).