Two Factor
Authentication form with six-digit code input.
<!-- Two Factor -->
<!-- An Alpine.js and Tailwind CSS component by https://pinemix.com -->
<section class="mx-auto w-full max-w-xl py-6">
<div
class="rounded-xl border border-zinc-200 text-center dark:border-zinc-800 dark:text-zinc-100"
>
<div class="p-5 sm:p-8 md:p-12">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="hi-outline hi-lock-closed mb-5 inline-block size-6 opacity-75"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"
/>
</svg>
<h1 class="mb-2 text-2xl font-bold">Two-Factor Authentication</h1>
<h2 class="mb-8 text-sm text-zinc-600 dark:text-zinc-400">
Please confirm your account by entering the verification code sent to
your mobile number ending in **147.
</h2>
<form
x-data="{
autoSubmit: false,
isNumber(value) {
if (value.match(/^[0-9]$/g)) {
return true;
}
},
handleSubmit() {
$refs.twoFactorButton.focus();
// Submit form
if (this.autoSubmit) {
$refs.twoFactorForm.submit();
}
},
handlePaste(e) {
let num = e.clipboardData.getData('text/plain').trim();
if (num.length === 6 && num.match(/^[0-9]+$/g)) {
$refs.num1.value = num.charAt(0);
$refs.num2.value = num.charAt(1);
$refs.num3.value = num.charAt(2);
$refs.num4.value = num.charAt(3);
$refs.num5.value = num.charAt(4);
$refs.num6.value = num.charAt(5);
this.handleSubmit();
}
},
}"
x-ref="twoFactorForm"
class="space-y-6"
onsubmit="return false;"
>
<div class="inline-flex items-center gap-1.5">
<input
x-ref="num1"
x-on:input.change="() => { isNumber($refs.num1.value) ? $refs.num2.focus() : $refs.num1.value = '' }"
x-on:paste="handlePaste"
type="text"
id="num1"
name="num1"
maxlength="1"
autofocus
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
<input
x-ref="num2"
x-on:input.change="() => { isNumber($refs.num2.value) ? $refs.num3.focus() : $refs.num2.value = '' }"
x-on:keydown.backspace="() => { $refs.num2.value === '' ? $refs.num1.focus() : null }"
x-on:paste="handlePaste"
type="text"
id="num2"
name="num2"
maxlength="1"
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
<input
x-ref="num3"
x-on:input.change="() => { isNumber($refs.num3.value) ? $refs.num4.focus() : $refs.num3.value = '' }"
x-on:keydown.backspace="() => { $refs.num3.value === '' ? $refs.num2.focus() : null }"
x-on:paste="handlePaste"
type="text"
id="num3"
name="num3"
maxlength="1"
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
<span class="text-sm text-zinc-400 dark:text-zinc-600">-</span>
<input
x-ref="num4"
x-on:input.change="() => { isNumber($refs.num4.value) ? $refs.num5.focus() : $refs.num4.value = '' }"
x-on:keydown.backspace="() => { $refs.num4.value === '' ? $refs.num3.focus() : null }"
x-on:paste="handlePaste"
type="text"
id="num4"
name="num4"
maxlength="1"
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
<input
x-ref="num5"
x-on:input.change="() => { isNumber($refs.num5.value) ? $refs.num6.focus() : $refs.num5.value = '' }"
x-on:keydown.backspace="() => { $refs.num5.value === '' ? $refs.num4.focus() : null }"
x-on:paste="handlePaste"
type="text"
id="num5"
name="num5"
maxlength="1"
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
<input
x-ref="num6"
x-on:input.change="() => { isNumber($refs.num6.value) ? handleSubmit() : $refs.num6.value = '' }"
x-on:keydown.backspace="() => { $refs.num6.value === '' ? $refs.num5.focus() : null }"
x-on:paste="handlePaste"
type="text"
id="num6"
name="num6"
maxlength="1"
class="block w-9 rounded-lg border border-zinc-200 px-2 py-1.5 text-center text-sm/6 placeholder-zinc-500 focus:border-zinc-500 focus:ring focus:ring-zinc-500/50 dark:border-zinc-600 dark:bg-transparent dark:placeholder-zinc-400 dark:focus:border-zinc-500"
/>
</div>
<div>
<button
x-ref="twoFactorButton"
type="submit"
class="inline-flex min-w-32 items-center justify-center gap-2 rounded-lg border border-zinc-800 bg-zinc-800 px-3 py-2 text-sm font-medium leading-5 text-white hover:border-zinc-900 hover:bg-zinc-900 hover:text-white focus:outline-none focus:ring-2 focus:ring-zinc-500/50 active:border-zinc-700 active:bg-zinc-700 dark:border-zinc-700/50 dark:bg-zinc-700/50 dark:ring-zinc-700/50 dark:hover:border-zinc-700 dark:hover:bg-zinc-700/75 dark:active:border-zinc-700/50 dark:active:bg-zinc-700/50"
>
<span>Verify code</span>
</button>
</div>
</form>
<div class="mt-5 text-sm text-zinc-500 dark:text-zinc-400">
Haven't received it?
<a
href="javascript:void(0)"
class="font-medium text-teal-700 underline decoration-teal-500/50 underline-offset-2 hover:text-teal-900 dark:text-teal-300 dark:decoration-teal-400/50 dark:hover:text-teal-100"
>
Resend a new one
</a>
</div>
</div>
</div>
<!-- END Form -->
</section>
<!-- END Two Factor -->
Props
The available data properties for this component.
Property | Default | Description |
---|---|---|
autoSubmit | false | Auto submits the form once all inputs are completed |