OTP Code Input
A copy-paste login & auth component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
Login & AuthHTMLJavaScriptCSSany framework
Copy into your project
HTML
<div class="nuda-otp">
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
<input class="nuda-otp__cell" maxlength="1" inputmode="numeric" />
</div>JavaScript
// Auto-advance + paste support for the OTP cells
const cells = document.querySelectorAll('.nuda-otp__cell');
cells.forEach((cell, i) => {
cell.addEventListener('input', (e) => {
if (e.target.value.length === 1) {
cell.classList.add('is-filled');
cells[i + 1]?.focus();
} else {
cell.classList.remove('is-filled');
}
});
cell.addEventListener('keydown', (e) => {
if (e.key === 'Backspace' && !cell.value) cells[i - 1]?.focus();
});
});
cells[0].addEventListener('paste', (e) => {
const text = (e.clipboardData?.getData('text') ?? '').slice(0, cells.length);
e.preventDefault();
[...text].forEach((char, i) => {
if (cells[i]) {
cells[i].value = char;
cells[i].classList.add('is-filled');
}
});
cells[Math.min(text.length, cells.length - 1)]?.focus();
});CSS
.nuda-otp { display: flex; gap: 8px; }
.nuda-otp__cell {
width: 40px;
height: 50px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
color: #fafafa;
text-align: center;
font-size: 18px;
font-weight: 600;
outline: none;
transition: border-color 0.2s, box-shadow 0.25s, transform 0.2s, background 0.25s;
}
.nuda-otp__cell:focus {
border-color: #e4ff54;
box-shadow: 0 0 0 3px rgba(228, 255, 84, 0.15);
transform: translateY(-2px);
}
.nuda-otp__cell.is-filled {
border-color: #e4ff54;
background: rgba(228, 255, 84, 0.06);
color: #e4ff54;
animation: nuda-otp-pop 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes nuda-otp-pop {
0% { transform: scale(0.85); }
60% { transform: scale(1.08); }
100% { transform: scale(1); }
}How to use OTP Code Input
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.