HTML & CSS for a One-Time Password Input

You know those One Time Password inputs? The UI is typically 4 or 6 numbers with individual inputs. Just from today…

Here’s the UI the Safeway app uses in the Pharmacy section to log in.
Here’s how Substack authenticates.

Brad Frost was blogging about them recently. They certainly have some issue! Here’s one he spells out that I agree with wholeheartedly:

I don’t like the pattern where each digit is its own text box. It’s an affordance that’s supposed to make things clearer, but it doesn’t (for me at least). Can I paste? Where do I paste? Is my paste going to carry over into all of the little boxes? Half the time there’s a dash in the code; does that get included?

It’s awfully tricky to get right, considering the user confusion that can happen before you’re interacting with those little boxes. And once you are, the experience better be awfully accommodating.

A while back I read an article by Phuoc Nguyen about them called Build an OTP input field. I’d say all-in-all, Phuoc did a good job. The design and user experience was considered, like using the arrow keys to move between the inputs and handling “paste”. I’d say accessibility too but I feel like this is complicated enough of an interaction I can’t personally vouch for that.

But I’m also also like — damn — that’s complicated. That’s a lot of JavaScript code. Why is this so hard? And what would happen without JavaScript? Seems like it would be a pretty gnarly experience. A particular thing that makes it hard is making each character a separate <input /> in the HTML.

<div class="otp">
    <input type="text" maxlength="1" />
    <input type="text" maxlength="1" />
    <input type="text" maxlength="1" />
    <input type="text" maxlength="1" />
</div>Code language: HTML, XML (xml)

That complicates validation, input, pasting, accessibility, navigation… literally everything.

And then I was like… why can’t this just be one input? The rectangles behind the numbers is just visual theater. Just a bit of trendy decoration. It’s just a styling concern, not a semantic, usability, or any other concern.

So I was like… I’m just gonna make those rectangles background-images and see if that works. So I built a demo, but it had a flaw: as you typed the last character, the value would kinda slide one direction and look bad. You can see it here.

But I posed the challenge in our ShopTalk Discord and others had some ideas. Josh Collingsworth had an idea where you could cover up some area at the end and prevent the movement issue (the yellow block would be white or whatever covers up properly). Alex Fimion did it a smidge cleaner by covering the last bit with background-image instead of a pseudo-element. Here’s that:

Is that better than the 4-inputs approach?

I’m giving an only-slightly-hesitant thumbs up 👍. My hesitation is that in order for this to look right, there is a lot of “magic number” usage. That is, numbers that are just visually tweaked by hand to make it all work, and based on finicky things like font metrics (which might change over time and with different fonts) rather than hard foundational layout.

So let’s call this a pretty good take. I think when you consider the HTML used alone you can see using a one-input approach feels best:

<input
  required
  type="text"
  autocomplete="one-time-code"
  inputmode="numeric"
  maxlength="4"
  pattern="\d{4}"
>Code language: HTML, XML (xml)

In fact, if I started having problems with the look of the “rectangles behind the numbers” approach, I’d just make it a big ol’ single input without the individual rectangles. Like I said, I feel those are just something of a design trend anyway.

What does AI want to do?

Just as a fun little exercise, I used the very generic prompt create a 4 digit PIN input UI with zero rounds of feedback/iteration across a number of different scaffolding tools.

v0 used TSX and four individual inputs and tried to help with dealing with the complex UX. It worked decently once, then two more tries it tried using shadcn and some toast library and all kinds of stuff and just failed to run at all.
Copilot wanted four individual inputs and helped with the moving to the next input after any character typed, but none of the other issues.
Cascade (in Windsurf) went with a single input (!) and got the HTML pretty decent.
Bolt.new used a React/TSX/Tailwind approach with four inputs and handled pasting, input moving, etc pretty nicely.

I’m fairly confident that if you provided a more elaborate prompt with more specific requirements and were willing to go through rounds of iteration, you could get what you want out of these tools. I just found it interesting that by default, based on the code they were trained on, that what you get tends to focus on using multiple inputs, not to mention a heap of tools you don’t ask for.

It's time to take your JavaScript to the next level

2 responses to “HTML & CSS for a One-Time Password Input”

  1. Temani Afif Temani Afif says:

    I made something similar using gradients without a lot of magic numbers but CSS variables to control everything: https://css-tip.com/single-digit-inputs/

Leave a Reply

Your email address will not be published. Required fields are marked *

Did you know?

Frontend Masters Donates to open source projects. $363,806 contributed to date.