10 / 28
All 28 in 28 Tailwind CSS Input Fields
- 01Tailwind Floating Label Input
- 02Tailwind Underline Sweep Input
- 03Tailwind Neon Glow Input
- 04Tailwind Frosted Glass Input
- 05Tailwind Notched Outline Input
- 06Tailwind Drawn Border Input
- 07Tailwind Inline Validation Input
- 08Tailwind Search Input With Clear
- 09Tailwind Password Strength Meter
- 10Tailwind OTP Code Input
- 11Tailwind Rotating Placeholder Input
- 12Tailwind Tag Chip Input
- 13Tailwind Phone Input With Country
- 14Tailwind Stepper Number Input
- 15Tailwind File Drop Zone
- 16Tailwind Currency Input
- 17Tailwind Native Date Input
- 18Tailwind Time Range Input
- 19Tailwind Colour Picker Input
- 20Tailwind Voice Search Input
- 21Tailwind Email Suggestion Combobox
- 22Tailwind Auto-Grow Textarea
- 23Tailwind Inline Edit Field
- 24Tailwind Range Slider
- 25Tailwind URL Input With Protocol
- 26Tailwind Credit Card Input
- 27Tailwind Password Toggle Input
- 28Tailwind Brutalist Input
Tailwind OTP Code Input
Six single-character boxes in a <fieldset>, with auto-advance, backspace step-back and paste-to-fill. autocomplete="one-time-code" on the first box is what surfaces the iOS SMS suggestion.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tif-10 grid min-h-svh w-full place-items-center bg-tif10-bg px-6 py-14 font-tif10-mono" aria-label="OTP code input demo">
<fieldset class="grid w-full max-w-70 grid-cols-6 gap-2 border-0 p-0">
<legend class="sr-only">Verification code</legend>
<input type="text" inputmode="numeric" maxlength="1" autocomplete="one-time-code" pattern="[0-9]" aria-label="Digit 1" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 2" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 3" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 4" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 5" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 6" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
</fieldset>
</div><div class="tif-10 grid min-h-svh w-full place-items-center bg-tif10-bg px-6 py-14 font-tif10-mono" aria-label="OTP code input demo">
<fieldset class="grid w-full max-w-70 grid-cols-6 gap-2 border-0 p-0">
<legend class="sr-only">Verification code</legend>
<input type="text" inputmode="numeric" maxlength="1" autocomplete="one-time-code" pattern="[0-9]" aria-label="Digit 1" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 2" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 3" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 4" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 5" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 6" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
</fieldset>
</div>@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}(function () {
document.querySelectorAll('.tif-10').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var inputs = Array.prototype.slice.call(root.querySelectorAll('[data-tif10]'));
inputs.forEach(function (input, i) {
input.addEventListener('input', function () {
input.value = (input.value || '').replace(/\D/g, '').slice(0, 1);
if (input.value && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('keydown', function (e) {
if (e.key === 'Backspace' && !input.value && inputs[i - 1]) {
inputs[i - 1].focus();
}
if (e.key === 'ArrowLeft' && inputs[i - 1]) inputs[i - 1].focus();
if (e.key === 'ArrowRight' && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('paste', function (e) {
var data = (e.clipboardData || window.clipboardData).getData('text');
var digits = (data || '').replace(/\D/g, '').slice(0, inputs.length - i);
if (!digits) return;
e.preventDefault();
for (var j = 0; j < digits.length; j++) {
if (inputs[i + j]) inputs[i + j].value = digits[j];
}
inputs[Math.min(i + digits.length, inputs.length - 1)].focus();
});
});
});
})();(function () {
document.querySelectorAll('.tif-10').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var inputs = Array.prototype.slice.call(root.querySelectorAll('[data-tif10]'));
inputs.forEach(function (input, i) {
input.addEventListener('input', function () {
input.value = (input.value || '').replace(/\D/g, '').slice(0, 1);
if (input.value && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('keydown', function (e) {
if (e.key === 'Backspace' && !input.value && inputs[i - 1]) {
inputs[i - 1].focus();
}
if (e.key === 'ArrowLeft' && inputs[i - 1]) inputs[i - 1].focus();
if (e.key === 'ArrowRight' && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('paste', function (e) {
var data = (e.clipboardData || window.clipboardData).getData('text');
var digits = (data || '').replace(/\D/g, '').slice(0, inputs.length - i);
if (!digits) return;
e.preventDefault();
for (var j = 0; j < digits.length; j++) {
if (inputs[i + j]) inputs[i + j].value = digits[j];
}
inputs[Math.min(i + digits.length, inputs.length - 1)].focus();
});
});
});
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Bug fixed. The original's regex is double-escaped inside its template literal and reaches the page as
/\\D/g, which matches a literal backslash — so non-digits were never stripped. Single escape here. - Arrow-key navigation added: a six-box grid that only moves forward is a usability trap when you mistype box two.
- The seven-property visually-hidden legend becomes
sr-only. - The script is wrapped in an IIFE with a
data-boundguard, so pasting the snippet twice on a page is safe.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind OTP Code Input
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-otp-code-input/
Six single-character boxes in a <fieldset>, with auto-advance, backspace step-back and paste-to-fill. autocomplete="one-time-code" on the first box is what surfaces the iOS SMS suggestion.
## HTML
```html
<div class="tif-10 grid min-h-svh w-full place-items-center bg-tif10-bg px-6 py-14 font-tif10-mono" aria-label="OTP code input demo">
<fieldset class="grid w-full max-w-70 grid-cols-6 gap-2 border-0 p-0">
<legend class="sr-only">Verification code</legend>
<input type="text" inputmode="numeric" maxlength="1" autocomplete="one-time-code" pattern="[0-9]" aria-label="Digit 1" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 2" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 3" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 4" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 5" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 6" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
</fieldset>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.tif-10').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var inputs = Array.prototype.slice.call(root.querySelectorAll('[data-tif10]'));
inputs.forEach(function (input, i) {
input.addEventListener('input', function () {
input.value = (input.value || '').replace(/\D/g, '').slice(0, 1);
if (input.value && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('keydown', function (e) {
if (e.key === 'Backspace' && !input.value && inputs[i - 1]) {
inputs[i - 1].focus();
}
if (e.key === 'ArrowLeft' && inputs[i - 1]) inputs[i - 1].focus();
if (e.key === 'ArrowRight' && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('paste', function (e) {
var data = (e.clipboardData || window.clipboardData).getData('text');
var digits = (data || '').replace(/\D/g, '').slice(0, inputs.length - i);
if (!digits) return;
e.preventDefault();
for (var j = 0; j < digits.length; j++) {
if (inputs[i + j]) inputs[i + j].value = digits[j];
}
inputs[Math.min(i + digits.length, inputs.length - 1)].focus();
});
});
});
})();
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind OTP Code Input
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-otp-code-input/
Six single-character boxes in a <fieldset>, with auto-advance, backspace step-back and paste-to-fill. autocomplete="one-time-code" on the first box is what surfaces the iOS SMS suggestion.
## HTML
```html
<div class="tif-10 grid min-h-svh w-full place-items-center bg-tif10-bg px-6 py-14 font-tif10-mono" aria-label="OTP code input demo">
<fieldset class="grid w-full max-w-70 grid-cols-6 gap-2 border-0 p-0">
<legend class="sr-only">Verification code</legend>
<input type="text" inputmode="numeric" maxlength="1" autocomplete="one-time-code" pattern="[0-9]" aria-label="Digit 1" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 2" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 3" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 4" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 5" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" aria-label="Digit 6" data-tif10
class="aspect-square w-full rounded-[10px] border border-white/10 bg-tif10-field text-center text-lg font-bold text-tif10-ink caret-tif10-acc outline-none transition-[border-color,background-color,transform] duration-150 focus:-translate-y-px focus:border-tif10-acc focus:bg-tif10-acc/10 motion-reduce:transition-none motion-reduce:focus:translate-y-0">
</fieldset>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tif10-bg: #12121a;
--color-tif10-bg: oklch(0.19 0.015 285);
--color-tif10-field: #1a1a22;
--color-tif10-field: oklch(0.23 0.015 285);
--color-tif10-ink: #f0eeff;
--color-tif10-ink: oklch(0.95 0.02 290);
--color-tif10-acc: #7c6cff;
--color-tif10-acc: oklch(0.62 0.2 288);
--font-tif10-mono: 'JetBrains Mono', ui-monospace, monospace;
}
.tif-10 {
width: 100%;
}
.tif-10 ::selection {
background: var(--color-tif10-acc);
color: #12121a;
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.tif-10').forEach(function (root) {
if (root.dataset.bound) return; root.dataset.bound = '1';
var inputs = Array.prototype.slice.call(root.querySelectorAll('[data-tif10]'));
inputs.forEach(function (input, i) {
input.addEventListener('input', function () {
input.value = (input.value || '').replace(/\D/g, '').slice(0, 1);
if (input.value && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('keydown', function (e) {
if (e.key === 'Backspace' && !input.value && inputs[i - 1]) {
inputs[i - 1].focus();
}
if (e.key === 'ArrowLeft' && inputs[i - 1]) inputs[i - 1].focus();
if (e.key === 'ArrowRight' && inputs[i + 1]) inputs[i + 1].focus();
});
input.addEventListener('paste', function (e) {
var data = (e.clipboardData || window.clipboardData).getData('text');
var digits = (data || '').replace(/\D/g, '').slice(0, inputs.length - i);
if (!digits) return;
e.preventDefault();
for (var j = 0; j < digits.length; j++) {
if (inputs[i + j]) inputs[i + j].value = digits[j];
}
inputs[Math.min(i + digits.length, inputs.length - 1)].focus();
});
});
});
})();
```How this works
Three listeners per box. input strips non-digits and advances, keydown catches Backspace on an empty box and steps back, paste runs the same strip across the clipboard so 123-456 and code: 123 456 both land correctly.
The autofill contract is exact. iOS Safari looks for autocomplete="one-time-code", spelled that way, on the first field; Chrome Android's WebOTP API honours the same hint. A typo here does not warn, it just silently never offers the code.
Make it yours
- Digit count: add or remove inputs — the script reads whatever it finds and adapts
grid-cols-6is the only thing to match. - Alphanumeric: drop
inputmode="numeric"and the digits-only strip. - Auto-submit: in the input handler, check
i === inputs.length - 1 && input.valueand callform.requestSubmit(). - Accent:
--color-tif10-accdrives caret, focus border and focus fill.
Gotchas — read before shipping
- iOS SMS autofill no-ops on any spelling other than
one-time-code— no shorthand, no typo. - Paste only survives
123-456because the regex strips non-digits first. Remove it and users see empty boxes. - Calling
focus()mid-render inside React loses the caret — wire this through refs and an effect, not inline JSX.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
iOS Safari 12+ surfaces the SMS suggestion via autocomplete="one-time-code"; Chrome Android 84+ implements WebOTP for the same UX.