30 Tailwind CSS Login Forms
25 / 30

All 30 in 30 Tailwind CSS Login Forms

  1. 01Tailwind Split-Screen Brand Hero Login Form
  2. 02Tailwind Multi-Tenant Workspace Account Switcher
  3. 03Tailwind Floating Label Login Form with peer Variants
  4. 04Tailwind Web3 Wallet Connect Login Grid
  5. 05Tailwind Minimalist Fintech Login Card with Layered Shadow
  6. 06Tailwind OAuth Social Login Form with Brand SVG Buttons
  7. 07Tailwind Passwordless Magic Link Login with data-state Machine
  8. 08Tailwind Aurora Glow Login Card with Drifting Blur Blobs
  9. 09Tailwind Neon Synthwave Arcade Login Form
  10. 10Tailwind Sliding Login and Sign Up Panel with peer-checked
  11. 11Tailwind 3D Flip Login Card with Password Reset Back Face
  12. 12Tailwind Terminal CLI Prompt Login Form
  13. 13Tailwind Floating Pill Login Widget Docked Over App Content
  14. 14Tailwind Gradient Border Login Card with background-clip
  15. 15Tailwind Slide-Over Side Panel Login without JavaScript
  16. 16Tailwind Social-First Sign Up with Brand-Compliant Buttons
  17. 17Tailwind Animated Mascot Login that Covers Its Eyes
  18. 18Tailwind Accessible Compact Login Modal with Scale-In
  19. 19Tailwind Glassmorphism Login Card with backdrop-filter
  20. 20Tailwind Multi-Step Login Wizard with Radio-Driven Track
  21. 21Tailwind Animated Conic Gradient Border Login Card
  22. 22Tailwind Secure Vault Login with user-invalid Error State
  23. 23Tailwind Neo-Brutalist Login Form with Hard Offset Shadow
  24. 24Tailwind Show Hide Password Toggle with aria-pressed
  25. 25Tailwind OTP Code Input Grid with Auto-Advance
  26. 26Tailwind Password Strength Meter with data-score Variants
  27. 27Tailwind Numeric PIN Pad Login for Kiosks and POS
  28. 28Tailwind Inline Form Validation with user-valid and user-invalid
  29. 29Tailwind Particle Constellation Login over Canvas
  30. 30Tailwind Biometric Face ID Prompt with Pulse Ripples
Tailwind + JSMIT licensed

Tailwind OTP Code Input Grid with Auto-Advance

The multi-factor confirmation screen: six evenly-sized digit boxes that auto-advance as you type, accept a full paste, and step back on Backspace. CSS holds the filled state via not-placeholder-shown:, so the boxes light up without the script tracking any visual state at all.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="tlf-25 w-full min-h-svh grid place-items-center bg-tlf25-stage font-tlf25-sans p-6">
  <form novalidate class="tlf-25-form w-[min(420px,100%)] p-8 text-center bg-white border border-tlf25-edge rounded-[18px] shadow-[0_22px_50px_-38px_oklch(0.25_0.06_265/0.5)]">
    <h1 class="text-[22px] font-bold text-tlf25-ink">Verify your identity</h1>
    <p class="mt-1.5 mb-6 text-[13.5px]/[1.5] text-tlf25-mut">Enter the 6-digit code we sent to your email.</p>
    <div role="group" aria-label="6-digit verification code" class="grid grid-cols-6 gap-2 mb-6">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" autocomplete="one-time-code" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 1">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 2">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 3">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 4">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 5">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 6">
    </div>
    <button type="submit" class="w-full h-[50px] rounded-xl border-0 cursor-pointer bg-tlf25-acc text-white text-[15px] font-semibold transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.99] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf25-ring motion-reduce:transition-none motion-reduce:active:scale-100">Verify</button>
    <p class="mt-4 text-[13px] text-tlf25-mut">Didn&rsquo;t get it? <a href="#" class="font-semibold text-tlf25-acc no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf25-acc">Resend code</a></p>
  </form>
</section>
@import "tailwindcss";

@theme {
  --color-tlf25-stage: #eef1f7;
  --color-tlf25-stage: oklch(0.95 0.008 265);
  --color-tlf25-edge: #e5e9f2;
  --color-tlf25-edge: oklch(0.92 0.008 265);
  --color-tlf25-fieldedge: #d5dbe8;
  --color-tlf25-fieldedge: oklch(0.87 0.012 265);
  --color-tlf25-field: #fafbfe;
  --color-tlf25-field: oklch(0.99 0.003 265);
  --color-tlf25-tint: #eef1fc;
  --color-tlf25-tint: oklch(0.56 0.18 265 / 0.06);
  --color-tlf25-ink: #1a2237;
  --color-tlf25-ink: oklch(0.25 0.04 265);
  --color-tlf25-mut: #666e88;
  --color-tlf25-mut: oklch(0.52 0.03 265);
  --color-tlf25-ph: #bcc3d4;
  --color-tlf25-ph: oklch(0.8 0.015 265);
  --color-tlf25-acc: #4159cd;
  --color-tlf25-acc: oklch(0.51 0.18 265);
  --color-tlf25-halo: #bfc9f0;
  --color-tlf25-halo: oklch(0.51 0.18 265 / 0.2);
  --color-tlf25-ring: #94a2ea;
  --color-tlf25-ring: oklch(0.51 0.18 265 / 0.5);
  --font-tlf25-sans: 'DM Sans', system-ui, sans-serif;
}

.tlf-25 {
  width: 100%;
  min-height: 100svh;
}
const boxes = [...document.querySelectorAll('.tlf-25-box')];

boxes.forEach((box, i) => {
  box.addEventListener('input', () => {
    box.value = box.value.replace(/[^0-9]/g, '').slice(0, 1);
    if (box.value && i < boxes.length - 1) boxes[i + 1].focus();
  });

  box.addEventListener('keydown', (e) => {
    if (e.key === 'Backspace' && !box.value && i > 0) boxes[i - 1].focus();
    if (e.key === 'ArrowLeft' && i > 0) boxes[i - 1].focus();
    if (e.key === 'ArrowRight' && i < boxes.length - 1) boxes[i + 1].focus();
  });

  box.addEventListener('paste', (e) => {
    e.preventDefault();
    const digits = (e.clipboardData.getData('text') || '').replace(/[^0-9]/g, '').split('');
    boxes.forEach((b, j) => { if (digits[j] != null) b.value = digits[j]; });
    boxes[Math.min(digits.length, boxes.length) - 1]?.focus();
  });
});

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tlf25-stage: #eef1f7;
  --color-tlf25-stage: oklch(0.95 0.008 265);
  --color-tlf25-edge: #e5e9f2;
  --color-tlf25-edge: oklch(0.92 0.008 265);
  --color-tlf25-fieldedge: #d5dbe8;
  --color-tlf25-fieldedge: oklch(0.87 0.012 265);
  --color-tlf25-field: #fafbfe;
  --color-tlf25-field: oklch(0.99 0.003 265);
  --color-tlf25-tint: #eef1fc;
  --color-tlf25-tint: oklch(0.56 0.18 265 / 0.06);
  --color-tlf25-ink: #1a2237;
  --color-tlf25-ink: oklch(0.25 0.04 265);
  --color-tlf25-mut: #666e88;
  --color-tlf25-mut: oklch(0.52 0.03 265);
  --color-tlf25-ph: #bcc3d4;
  --color-tlf25-ph: oklch(0.8 0.015 265);
  --color-tlf25-acc: #4159cd;
  --color-tlf25-acc: oklch(0.51 0.18 265);
  --color-tlf25-halo: #bfc9f0;
  --color-tlf25-halo: oklch(0.51 0.18 265 / 0.2);
  --color-tlf25-ring: #94a2ea;
  --color-tlf25-ring: oklch(0.51 0.18 265 / 0.5);
  --font-tlf25-sans: 'DM Sans', system-ui, sans-serif;
}

.tlf-25 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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=DM+Sans:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • not-placeholder-shown: is v4's positive variant for :not(:placeholder-shown), and it is exactly what an OTP grid wants: the filled look is a function of the value, so the script never touches presentation.
  • Six near-identical class lists is the honest cost of utilities on repeated elements. In a real codebase this is one component rendered six times — the collection keeps them expanded because the whole point is to show the class list.
  • Arrow-key navigation was added to the handler: a six-box field where ←/→ do nothing feels broken, and the original only handled Backspace.
  • UI change from the CSS original: DM Sans replaces the system stack, the muted sub-copy lifted from #767d97 to oklch(0.52 0.03 265), boxes went 52px → h-13 with a 48px narrow variant, and the filled tint is now a token rather than an inline alpha colour.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 Grid with Auto-Advance
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-otp-code-input-grid-auto-advance/

The multi-factor confirmation screen: six evenly-sized digit boxes that auto-advance as you type, accept a full paste, and step back on Backspace. CSS holds the filled state via not-placeholder-shown:, so the boxes light up without the script tracking any visual state at all.
## HTML
```html
<section class="tlf-25 w-full min-h-svh grid place-items-center bg-tlf25-stage font-tlf25-sans p-6">
  <form novalidate class="tlf-25-form w-[min(420px,100%)] p-8 text-center bg-white border border-tlf25-edge rounded-[18px] shadow-[0_22px_50px_-38px_oklch(0.25_0.06_265/0.5)]">
    <h1 class="text-[22px] font-bold text-tlf25-ink">Verify your identity</h1>
    <p class="mt-1.5 mb-6 text-[13.5px]/[1.5] text-tlf25-mut">Enter the 6-digit code we sent to your email.</p>
    <div role="group" aria-label="6-digit verification code" class="grid grid-cols-6 gap-2 mb-6">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" autocomplete="one-time-code" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 1">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 2">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 3">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 4">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 5">
      <input class="tlf-25-box min-w-0 h-13 max-[420px]:h-12 text-center text-[22px] max-[420px]:text-[19px] font-bold text-tlf25-ink rounded-xl bg-tlf25-field border-[1.5px] border-tlf25-fieldedge transition-[border-color,box-shadow,background-color,translate] duration-150 placeholder:text-tlf25-ph not-placeholder-shown:border-tlf25-acc not-placeholder-shown:bg-tlf25-tint focus-visible:outline-none focus-visible:border-tlf25-acc focus-visible:ring-4 focus-visible:ring-tlf25-halo focus-visible:-translate-y-0.5 motion-reduce:transition-colors motion-reduce:focus-visible:translate-y-0" type="text" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="&bull;" aria-label="Digit 6">
    </div>
    <button type="submit" class="w-full h-[50px] rounded-xl border-0 cursor-pointer bg-tlf25-acc text-white text-[15px] font-semibold transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.99] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf25-ring motion-reduce:transition-none motion-reduce:active:scale-100">Verify</button>
    <p class="mt-4 text-[13px] text-tlf25-mut">Didn&rsquo;t get it? <a href="#" class="font-semibold text-tlf25-acc no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf25-acc">Resend code</a></p>
  </form>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tlf25-stage: #eef1f7;
  --color-tlf25-stage: oklch(0.95 0.008 265);
  --color-tlf25-edge: #e5e9f2;
  --color-tlf25-edge: oklch(0.92 0.008 265);
  --color-tlf25-fieldedge: #d5dbe8;
  --color-tlf25-fieldedge: oklch(0.87 0.012 265);
  --color-tlf25-field: #fafbfe;
  --color-tlf25-field: oklch(0.99 0.003 265);
  --color-tlf25-tint: #eef1fc;
  --color-tlf25-tint: oklch(0.56 0.18 265 / 0.06);
  --color-tlf25-ink: #1a2237;
  --color-tlf25-ink: oklch(0.25 0.04 265);
  --color-tlf25-mut: #666e88;
  --color-tlf25-mut: oklch(0.52 0.03 265);
  --color-tlf25-ph: #bcc3d4;
  --color-tlf25-ph: oklch(0.8 0.015 265);
  --color-tlf25-acc: #4159cd;
  --color-tlf25-acc: oklch(0.51 0.18 265);
  --color-tlf25-halo: #bfc9f0;
  --color-tlf25-halo: oklch(0.51 0.18 265 / 0.2);
  --color-tlf25-ring: #94a2ea;
  --color-tlf25-ring: oklch(0.51 0.18 265 / 0.5);
  --font-tlf25-sans: 'DM Sans', system-ui, sans-serif;
}

.tlf-25 {
  width: 100%;
  min-height: 100svh;
}
```

## JavaScript
```js
const boxes = [...document.querySelectorAll('.tlf-25-box')];

boxes.forEach((box, i) => {
  box.addEventListener('input', () => {
    box.value = box.value.replace(/[^0-9]/g, '').slice(0, 1);
    if (box.value && i < boxes.length - 1) boxes[i + 1].focus();
  });

  box.addEventListener('keydown', (e) => {
    if (e.key === 'Backspace' && !box.value && i > 0) boxes[i - 1].focus();
    if (e.key === 'ArrowLeft' && i > 0) boxes[i - 1].focus();
    if (e.key === 'ArrowRight' && i < boxes.length - 1) boxes[i + 1].focus();
  });

  box.addEventListener('paste', (e) => {
    e.preventDefault();
    const digits = (e.clipboardData.getData('text') || '').replace(/[^0-9]/g, '').split('');
    boxes.forEach((b, j) => { if (digits[j] != null) b.value = digits[j]; });
    boxes[Math.min(digits.length, boxes.length) - 1]?.focus();
  });
});
```

How this works

The script owns focus, CSS owns appearance. The handler moves focus on input, steps back on Backspace, and splits a pasted code across the boxes. It never adds a class — filled styling comes from whether the box has a value, which not-placeholder-shown: reads directly.

Paste is not optional. Users paste codes out of email. The paste handler strips non-digits, distributes what it finds, and focuses the last filled box; without it, this pattern is actively worse than a single text field.

The group announces as one field. role="group" with a label, plus per-box aria-label="Digit N" — so a screen reader says "6-digit verification code, Digit 1, edit text" rather than reading six unlabelled inputs.

Make it yours

  • Code length: add boxes, change grid-cols-6, and the script adapts (it reads the DOM).
  • Box size: h-13 and text-[22px]; the narrow variant drops both via max-[420px]:.
  • Accent: --color-tlf25-acc drives focus, filled border and the tint.
  • Auto-submit on the final digit — three lines in the input handler.

Gotchas — read before shipping

  • Support paste, and strip non-digits when you do.
  • autocomplete="one-time-code" belongs on the first box only; repeating it across all six confuses platform autofill.
  • Boxes need min-w-0 inside the grid, or the intrinsic input width blows the columns out on narrow screens.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Grid, :placeholder-shown and clipboard paste events are universally supported; the floor is Tailwind v4's generated sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…