One Time Password Input
There's surprisingly not a great out of the box solution to this that has all of the following:
- Support for borders around each digit.
- Support for auto populating from SMS
- Support for copy/paste
- Support for moving to the next input after each digit
- Support for using the arrow keys to move back and forth
Approach 1
This is the simplest approach and should be the go-to. Because it's a single input it supports everything natively, except for borders around each of the digits. It shows the number input on mobile, ensures there's only 4 characters (swap to 6 if necessary), autofocuses the input, checks for numericality, and auto-fills from SMS.
Code
input[autocomplete=one-time-code] {
border-bottom: 2px solid #f6f6f7;
width: 240px;
letter-spacing: .5ch;
font-size: 60px;
font-family: monospace;
font-weight: bold;
}
input[autocomplete=one-time-code]:focus {
outline: none;
border-bottom: 2px solid #191919;
}
<form>
<label for="otp" class="block" >Enter OTP</label>
<input id="otp" autofocus="true" placeholder="0000" required class="no-style" autocomplete="one-time-code" type="text" inputmode="numeric" maxlength="4" pattern="\d{4}" />
</form>
Approach 2
This approach is from Chris Coyier's blog post. This approach uses a single input, with a background pattern that gives the impression of multiple inputs. The downside is the "dangling" cursor at the end. If I had more time I'd solve this by either unfocusing the input with js after 4 characters, or by fixing the width and using overflow hidden to hide it.
Code
input.bordered[autocomplete="one-time-code"] {
--magic-number: 100px;
padding-left: 30px;
width: 500px;
overflow: hidden;
background-image: linear-gradient(#fff, #fff),
url("https://assets.codepen.io/3/rounded-rectangle.svg");
background-size: var(--magic-number);
background-position-x: right, left;
background-repeat: no-repeat, repeat-x;
border: 0;
height: var(--magic-number);
font-size: calc(0.6 * var(--magic-number));
font-family: monospace;
letter-spacing: calc(0.64 * var(--magic-number));
box-sizing: border-box;
overflow: hidden;
}
input.bordered[autocomplete="one-time-code"]:focus {
outline: none;
background-image: linear-gradient(#fff, #fff),
url("https://assets.codepen.io/729148/blue-rounded-rectangle.svg");
background-size: var(--magic-number);
background-position-x: right, left;
background-repeat: no-repeat, repeat-x;
}
<form class="pb-8">
<label for="otp2" class="block" >Enter OTP</label>
<input id="otp2" autofocus="true" placeholder="0000" required class="no-style bordered" autocomplete="one-time-code" type="text" inputmode="numeric" maxlength="4" pattern="\d{4}" />
</form>
Approach 3
Leaving this here as a placeholder for when I have time to write a version with multiple inputs that uses Mini.js for interactivity.
References
Add this to the <head> of your page