24 CSS Login Form Designs with Live Demos

OTP Verify

OTP / one-time-passcode login form. Six digit boxes with auto-advance, backspace step-back, paste-to-fill, and `autocomplete="one-time-code"` for SMS auto-suggest.

Light JS MIT licensed

OTP Verify the 19th of 24 designs in the 24 CSS Login Form Designs with Live Demos collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<form class="lf-otp" aria-label="One-time passcode entry">
  <h2 class="lf-otp-title">Enter code</h2>
  <p class="lf-otp-sub">We sent a 6-digit code to <strong>[email protected]</strong></p>
  <fieldset class="lf-otp-row">
    <legend class="lf-otp-legend">Verification code</legend>
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      autocomplete="one-time-code"
      pattern="[0-9]"
      aria-label="Digit 1"
      data-lf-otp
    />
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      pattern="[0-9]"
      aria-label="Digit 2"
      data-lf-otp
    />
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      pattern="[0-9]"
      aria-label="Digit 3"
      data-lf-otp
    />
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      pattern="[0-9]"
      aria-label="Digit 4"
      data-lf-otp
    />
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      pattern="[0-9]"
      aria-label="Digit 5"
      data-lf-otp
    />
    <input
      type="text"
      inputmode="numeric"
      maxlength="1"
      pattern="[0-9]"
      aria-label="Digit 6"
      data-lf-otp
    />
  </fieldset>
  <button type="submit" class="lf-otp-btn">Verify &amp; sign in</button>
  <p class="lf-otp-foot">Didn't get it? <a href="#">Resend code</a></p>
</form>
.lf-otp {
  width: 100%;
  max-width: 320px;
  background: #15151d;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 14px;
  padding: 26px 22px;
  display: grid;
  gap: 12px;
  text-align: center;
}
.lf-otp-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: #f0eeff;
}
.lf-otp-sub {
  margin: -4px 0 6px;
  font-size: 12px;
  color: #b8b6d4;
  line-height: 1.5;
}
.lf-otp-sub strong {
  color: #f0eeff;
  font-weight: 600;
}
.lf-otp-row {
  border: 0;
  padding: 0;
  margin: 4px 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
}
.lf-otp-legend {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.lf-otp-row input {
  width: 100%;
  box-sizing: border-box;
  aspect-ratio: 1 / 1;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  font-size: 18px;
  font-weight: 700;
  text-align: center;
  color: #f0eeff;
  outline: none;
  caret-color: #7c6cff;
  transition:
    border-color 0.15s,
    background 0.15s,
    transform 0.12s;
}
.lf-otp-row input:focus {
  border-color: #7c6cff;
  background: rgba(124, 108, 255, 0.08);
  transform: translateY(-1px);
}
.lf-otp-btn {
  margin-top: 4px;
  padding: 11px;
  background: linear-gradient(135deg, #7c6cff, #ff6c8a);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.lf-otp-foot {
  margin: 0;
  font-size: 11px;
  color: #b8b6d4;
}
.lf-otp-foot a {
  color: #a78bfa;
  text-decoration: none;
}

/* ─── tiny JS to auto-advance & step back ─── */
/*
document.querySelectorAll('[data-lf-otp]').forEach((input, i, all) => {
  input.addEventListener('input', () => {
    input.value = input.value.replace(/\\D/g, '').slice(0, 1);
    if (input.value && all[i + 1]) all[i + 1].focus();
  });
  input.addEventListener('keydown', e => {
    if (e.key === 'Backspace' && !input.value && all[i - 1]) all[i - 1].focus();
  });
});
*/
// OTP auto-advance + backspace navigation + paste
document.querySelectorAll('.lf-otp-row').forEach(function (row) {
  var inputs = Array.prototype.slice.call(row.querySelectorAll('[data-lf-otp]'));
  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();
      }
    });
    input.addEventListener('paste', function (e) {
      var data = (e.clipboardData || window.clipboardData).getData('text');
      var digits = (data || '').replace(/D/g, '').slice(0, inputs.length);
      if (!digits) return;
      e.preventDefault();
      for (var j = 0; j < digits.length; j++) {
        if (inputs[i + j]) inputs[i + j].value = digits[j];
      }
      var next = Math.min(i + digits.length, inputs.length - 1);
      inputs[next].focus();
    });
  });
});

Search CodeFronts

Loading…