Open demo

CSS code

HTML
<div class="lf">
<main class="lf-box">
  <div class="lf-shield" aria-hidden="true">
    <svg viewBox="0 0 24 24"><path d="M12 3l7 3v5c0 4.5-3 8.3-7 10-4-1.7-7-5.5-7-10V6l7-3z"/><path d="M9 12l2 2 4-4"/></svg>
  </div>
  <h1>Two-factor authentication</h1>
  <p class="lf-sub" id="totp-hint">Open your authenticator app and enter the 6-digit code for <strong>Northwind</strong>.</p>
  <form onsubmit="event.preventDefault()">
    <label class="lf-label" for="totp">Authentication code</label>
    <input class="lf-field lf-code" id="totp" name="code" type="text" inputmode="numeric" pattern="[0-9]{6}" maxlength="6"
           autocomplete="one-time-code" placeholder="000000" aria-describedby="totp-hint" required>
    <label class="lf-trust"><input type="checkbox" name="trust_device">
      <span>Trust this device for 30 days. Don't tick this on a shared computer.</span></label>
    <button class="lf-btn" type="submit">Verify and sign in</button>
  </form>
  <p class="lf-alt">Lost your device? <a href="#">Use a recovery code</a><br>
    Still stuck? <a href="#">Contact support</a></p>
</main>
</div>
CSS
/* Two-factor step: one numeric code field with autocomplete="one-time-code", a trust-this-device checkbox
   and a recovery route. The page wrapper .lf centres the card; drop its background if your layout has one. */
.lf{--lf-brand:#4f46e5;--lf-brand-dark:#4338ca;--lf-ink:#111827;--lf-muted:#6b7280;--lf-line:#e5e7eb;--lf-edge:#868c99;--lf-bg:#f5f6fa;
  min-height:100vh;display:grid;grid-template-columns:minmax(0,1fr);place-items:center;padding:28px 20px;
  background:var(--lf-bg);color:var(--lf-ink);line-height:normal;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.lf,.lf *,.lf *::before,.lf *::after{box-sizing:border-box}
.lf :where(h1,p,form){margin:0}
.lf :where(input,button){font:inherit}
.lf a{color:var(--lf-brand);text-decoration:none;font-weight:600}
.lf a:hover{text-decoration:underline}
.lf :focus-visible{outline:2px solid var(--lf-brand);outline-offset:2px}
.lf-box{width:100%;max-width:400px;background:#fff;border:1px solid var(--lf-line);border-radius:16px;
  padding:38px 34px;box-shadow:0 10px 34px rgba(17,24,39,.06)}
.lf-shield{width:52px;height:52px;border-radius:14px;background:#eef2ff;display:grid;place-items:center;margin-bottom:20px}
.lf-shield svg{width:26px;height:26px;stroke:var(--lf-brand);fill:none;stroke-width:2}
.lf h1{font-size:1.4rem;letter-spacing:-.02em;margin-bottom:7px}
.lf-sub{color:var(--lf-muted);font-size:.93rem;line-height:1.6;margin-bottom:26px}
.lf-label{display:block;font-size:.85rem;font-weight:600;margin-bottom:6px}
.lf-field{width:100%;padding:12px 14px;border:1px solid var(--lf-edge);border-radius:10px;font-size:.95rem;
  background:#fff;color:inherit;transition:border-color .15s,box-shadow .15s}
.lf-field:focus{outline:none;border-color:var(--lf-brand);box-shadow:0 0 0 3px rgba(79,70,229,.15)}
.lf-code{letter-spacing:.5em;text-align:center;font-size:1.3rem;font-weight:700;padding:14px}
.lf-code::placeholder{letter-spacing:.3em;font-weight:400;color:var(--lf-muted)}
.lf-trust{display:flex;align-items:flex-start;gap:10px;margin:18px 0 24px;font-size:.87rem;font-weight:600;
  color:var(--lf-muted);line-height:1.5;cursor:pointer}
.lf-trust input{flex-shrink:0;margin:2px 0 0;accent-color:var(--lf-brand);width:16px;height:16px}
.lf-btn{width:100%;padding:13px;border:0;border-radius:10px;background:var(--lf-brand);color:#fff;
  font-size:.97rem;font-weight:700;cursor:pointer;transition:background .15s}
.lf-btn:hover{background:var(--lf-brand-dark)}
.lf-alt{margin-top:22px;padding-top:20px;border-top:1px solid var(--lf-line);font-size:.88rem;color:var(--lf-muted);line-height:1.7}
@media (prefers-reduced-motion:reduce){.lf-field,.lf-btn{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="lf d-grid">
<main class="card w-100">
  <div class="card-body">
    <div class="lf-shield" aria-hidden="true">
      <svg viewBox="0 0 24 24"><path d="M12 3l7 3v5c0 4.5-3 8.3-7 10-4-1.7-7-5.5-7-10V6l7-3z"/><path d="M9 12l2 2 4-4"/></svg>
    </div>
    <h1>Two-factor authentication</h1>
    <p class="lf-sub text-body-secondary" id="totp-hint">Open your authenticator app and enter the 6-digit code for <strong>Northwind</strong>.</p>
    <form onsubmit="event.preventDefault()">
      <label class="form-label" for="totp">Authentication code</label>
      <input class="form-control lf-code" id="totp" name="code" type="text" inputmode="numeric" pattern="[0-9]{6}" maxlength="6"
             autocomplete="one-time-code" placeholder="000000" aria-describedby="totp-hint" required>
      <div class="form-check">
        <input class="form-check-input" type="checkbox" id="trust" name="trust_device">
        <label class="form-check-label" for="trust">Trust this device for 30 days. Don't tick this on a shared computer.</label>
      </div>
      <button class="btn btn-brand w-100" type="submit">Verify and sign in</button>
    </form>
    <p class="lf-alt text-body-secondary mb-0">Lost your device? <a href="#">Use a recovery code</a><br>
      Still stuck? <a href="#">Contact support</a></p>
  </div>
</main>
</div>
CSS
/* Bootstrap .card, .form-control, .form-check and .btn, recoloured through Bootstrap's CSS variables. */
.lf{--lf-brand:#4f46e5;--lf-brand-dark:#4338ca;--lf-ink:#111827;--lf-muted:#6b7280;--lf-line:#e5e7eb;--lf-edge:#868c99;
  --bs-body-color:var(--lf-ink);--bs-secondary-color:var(--lf-muted);--bs-border-color:var(--lf-edge);
  --bs-link-color-rgb:79,70,229;--bs-link-hover-color-rgb:67,56,202;
  min-height:100vh;grid-template-columns:minmax(0,1fr);place-items:center;padding:28px 20px;
  background:#f5f6fa;color:var(--lf-ink);line-height:normal}
.lf a{font-weight:600;text-decoration:none}
.lf a:hover{text-decoration:underline}
.lf :focus-visible{outline:2px solid var(--lf-brand);outline-offset:2px}
.lf .card{--bs-card-border-color:var(--lf-line);--bs-card-border-radius:16px;--bs-card-spacer-y:38px;--bs-card-spacer-x:34px;
  max-width:400px;box-shadow:0 10px 34px rgba(17,24,39,.06)}
.lf-shield{width:52px;height:52px;border-radius:14px;background:#eef2ff;display:grid;place-items:center;margin-bottom:20px}
.lf-shield svg{width:26px;height:26px;stroke:var(--lf-brand);fill:none;stroke-width:2}
.lf h1{font-size:1.4rem;font-weight:700;line-height:normal;letter-spacing:-.02em;margin-bottom:7px}
.lf-sub{font-size:.93rem;line-height:1.6;margin-bottom:26px}
.lf .form-label{font-size:.85rem;font-weight:600;margin-bottom:6px}
.lf .form-control{padding:12px 14px;border-radius:10px;font-size:.95rem;line-height:normal;transition:border-color .15s,box-shadow .15s}
.lf .form-control:focus{border-color:var(--lf-brand);box-shadow:0 0 0 3px rgba(79,70,229,.15);outline:none}
.lf .form-control::placeholder{color:var(--lf-muted)}
.lf-code{letter-spacing:.5em;text-align:center;font-size:1.3rem!important;font-weight:700;padding:14px!important}
.lf-code::placeholder{letter-spacing:.3em;font-weight:400}
.lf .form-check{display:flex;align-items:flex-start;gap:10px;padding:0;margin:18px 0 24px;min-height:0}
.lf .form-check-input{flex-shrink:0;float:none;margin:2px 0 0;width:16px;height:16px;cursor:pointer}
.lf .form-check-input:checked{background-color:var(--lf-brand);border-color:var(--lf-brand)}
.lf .form-check-input:focus{border-color:var(--lf-brand);box-shadow:none}
.lf .form-check-label{font-size:.87rem;font-weight:600;color:var(--lf-muted);line-height:1.5;cursor:pointer}
.lf .btn-brand{--bs-btn-line-height:normal;--bs-btn-border-width:0;--bs-btn-padding-y:13px;--bs-btn-font-size:.97rem;--bs-btn-font-weight:700;--bs-btn-border-radius:10px;
  --bs-btn-bg:var(--lf-brand);--bs-btn-border-color:var(--lf-brand);--bs-btn-color:#fff;
  --bs-btn-hover-bg:var(--lf-brand-dark);--bs-btn-hover-border-color:var(--lf-brand-dark);--bs-btn-hover-color:#fff;
  --bs-btn-active-bg:var(--lf-brand-dark);--bs-btn-active-border-color:var(--lf-brand-dark);--bs-btn-active-color:#fff;
  --bs-btn-focus-box-shadow:none;transition:background-color .15s}
.lf-alt{margin-top:22px;padding-top:20px;border-top:1px solid var(--lf-line);font-size:.88rem;line-height:1.7}
@media (prefers-reduced-motion:reduce){.lf .form-control,.lf .btn{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="grid min-h-screen grid-cols-[minmax(0,1fr)] place-items-center bg-page px-5 py-7 font-[-apple-system,BlinkMacSystemFont,'Segoe_UI',Roboto,Helvetica,Arial,sans-serif] leading-[normal] text-ink">
<main class="w-full max-w-[400px] rounded-2xl border border-line bg-white px-[34px] py-[38px] shadow-[0_10px_34px_rgba(17,24,39,.06)]">
  <div class="mb-5 grid size-[52px] place-items-center rounded-[14px] bg-[#eef2ff]" aria-hidden="true">
    <svg class="size-[26px] fill-none stroke-brand stroke-2" viewBox="0 0 24 24"><path d="M12 3l7 3v5c0 4.5-3 8.3-7 10-4-1.7-7-5.5-7-10V6l7-3z"/><path d="M9 12l2 2 4-4"/></svg>
  </div>
  <h1 class="mb-[7px] text-[1.4rem] font-bold tracking-[-.02em]">Two-factor authentication</h1>
  <p class="mb-[26px] text-[.93rem] leading-[1.6] text-muted" id="totp-hint">Open your authenticator app and enter the 6-digit code for <strong>Northwind</strong>.</p>
  <form onsubmit="event.preventDefault()">
    <label class="mb-1.5 block text-[.85rem] font-semibold" for="totp">Authentication code</label>
    <input class="w-full rounded-[10px] border border-edge bg-white p-3.5 text-center text-[1.3rem] font-bold tracking-[.5em] transition-[border-color,box-shadow] duration-150 placeholder:font-normal placeholder:tracking-[.3em] placeholder:text-muted focus:border-brand focus:shadow-[0_0_0_3px_rgba(79,70,229,.15)] focus:outline-none motion-reduce:transition-none"
           id="totp" name="code" type="text" inputmode="numeric" pattern="[0-9]{6}" maxlength="6"
           autocomplete="one-time-code" placeholder="000000" aria-describedby="totp-hint" required>
    <label class="mt-[18px] mb-6 flex cursor-pointer items-start gap-2.5 text-[.87rem] leading-normal font-semibold text-muted"><input class="mt-0.5 size-4 shrink-0 accent-brand focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand" type="checkbox" name="trust_device">
      <span>Trust this device for 30 days. Don't tick this on a shared computer.</span></label>
    <button class="w-full cursor-pointer rounded-[10px] bg-brand p-[13px] text-[.97rem] font-bold text-white transition-[background-color,border-color,color] duration-150 hover:bg-brand-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand motion-reduce:transition-none" type="submit">Verify and sign in</button>
  </form>
  <p class="mt-[22px] border-t border-line pt-5 text-[.88rem] leading-[1.7] text-muted">Lost your device? <a class="font-semibold text-brand hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand" href="#">Use a recovery code</a><br>
    Still stuck? <a class="font-semibold text-brand hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand" href="#">Contact support</a></p>
</main>
</div>
CSS
@theme {
  --color-brand: #4f46e5;
  --color-brand-dark: #4338ca;
  --color-ink: #111827;
  --color-muted: #6b7280;
  --color-line: #e5e7eb;
  --color-edge: #868c99;
  --color-page: #f5f6fa;
}

About this set

The second step of a sign-in that uses an authenticator app. A shield icon and a short line tell the person which account the code is for, then one large, centred, letter-spaced field takes the six digits, a checkbox offers to trust the device for 30 days with a warning about shared computers, and a single button verifies and signs in. Below a divider sit the escape routes people need when their phone is missing: a recovery-code link and a support link. Use it after a correct password when the account has app-based two-factor turned on. The field is a text input with inputmode=”numeric”, pattern=”[0-9]{6}” and maxlength=”6″ rather than type=”number”, so leading zeros survive, mobile keyboards show digits and autocomplete=”one-time-code” lets iOS and Android offer a code from a text message or a password manager. The instruction line is tied to the field with aria-describedby, so the six-digit requirement is read out with the label. The Bootstrap version uses .card, .form-control, .form-check and .btn; the Tailwind version is utilities only.

What’s included

  • Single six-digit field with inputmode="numeric" and autocomplete="one-time-code"
  • Trust-this-device checkbox with a shared-computer warning
  • Recovery-code and support links for a lost phone
  • Instruction text linked to the field with aria-describedby
  • Input border darkened to #868c99 so the field edge reaches 3:1

Accessibility

The code field has a visible label and is described by the instruction line, and the checkbox is labelled by its full sentence. Every control is reachable with Tab; the field shows a brand-coloured border and ring on focus, and buttons, links and the checkbox get a 2px outline. The field and button transitions are removed under prefers-reduced-motion.

Customise it

Change --lf-brand, --lf-edge and --lf-bg on .lf (plain CSS and Bootstrap), or the --color-brand, --color-edge and --color-page theme colours (Tailwind). The letter spacing of the code lives on .lf-code or the tracking-[.5em] utility.