30 Tailwind CSS Login Forms
20 / 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
Pure TailwindMIT licensed

Tailwind Multi-Step Login Wizard with Radio-Driven Track

A multi-stage sign-in that lowers upfront resistance: email, then password, then a security code, with inactive panels sliding off a translated track. Three radios hold the stage, Next/Back are their labels, and the port's interesting constraint is that Tailwind's peer only reaches later siblings — which is exactly how this markup is already arranged.

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-20 w-full min-h-svh grid place-items-center bg-tlf20-stage font-tlf20-sans p-6">
  <form novalidate class="w-[min(380px,100%)] p-7 bg-white border border-tlf20-edge rounded-[18px] overflow-hidden shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.5)]">
    <input type="radio" name="tlf-20-step" id="tlf-20-s1" checked class="peer/s1 sr-only" aria-label="Step 1 of 3: email">
    <input type="radio" name="tlf-20-step" id="tlf-20-s2" class="peer/s2 sr-only" aria-label="Step 2 of 3: password">
    <input type="radio" name="tlf-20-step" id="tlf-20-s3" class="peer/s3 sr-only" aria-label="Step 3 of 3: security code">
    <div aria-hidden="true" class="h-1.5 mb-6 rounded-full bg-tlf20-track overflow-hidden">
      <span class="block h-full w-1/3 rounded-full bg-tlf20-acc transition-[width] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked/s2:w-2/3 peer-checked/s3:w-full motion-reduce:transition-none"></span>
    </div>
    <div class="overflow-hidden">
      <div class="flex w-[300%] transition-transform duration-450 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked/s2:-translate-x-1/3 peer-checked/s3:-translate-x-2/3 motion-reduce:transition-none">
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Your email</legend>
          <input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <label for="tlf-20-s2" class="grid place-items-center h-12 rounded-xl cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter] duration-150 hover:brightness-110">Continue</label>
        </fieldset>
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Your password</legend>
          <input type="password" autocomplete="current-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <div class="flex gap-2.5">
            <label for="tlf-20-s1" class="grid place-items-center basis-[92px] h-12 rounded-xl cursor-pointer bg-white border-[1.5px] border-tlf20-fieldedge text-tlf20-mut text-[15px] font-semibold transition-colors duration-150 hover:bg-tlf20-hover">Back</label>
            <label for="tlf-20-s3" class="grid place-items-center flex-1 h-12 rounded-xl cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter] duration-150 hover:brightness-110">Continue</label>
          </div>
        </fieldset>
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Security code</legend>
          <input type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="6-digit code" required class="h-12 px-3.5 rounded-xl text-[15px] tracking-[0.2em] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:tracking-normal placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <div class="flex gap-2.5">
            <label for="tlf-20-s2" class="grid place-items-center basis-[92px] h-12 rounded-xl cursor-pointer bg-white border-[1.5px] border-tlf20-fieldedge text-tlf20-mut text-[15px] font-semibold transition-colors duration-150 hover:bg-tlf20-hover">Back</label>
            <button type="submit" class="flex-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf20-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
          </div>
        </fieldset>
      </div>
    </div>
  </form>
</section>
@import "tailwindcss";

@theme {
  --color-tlf20-stage: #eef1f7;
  --color-tlf20-stage: oklch(0.95 0.008 265);
  --color-tlf20-edge: #e5e9f2;
  --color-tlf20-edge: oklch(0.92 0.008 265);
  --color-tlf20-fieldedge: #d8deeb;
  --color-tlf20-fieldedge: oklch(0.88 0.012 265);
  --color-tlf20-field: #fafbfe;
  --color-tlf20-field: oklch(0.99 0.003 265);
  --color-tlf20-hover: #f2f5fb;
  --color-tlf20-hover: oklch(0.97 0.006 265);
  --color-tlf20-track: #e6eaf3;
  --color-tlf20-track: oklch(0.93 0.008 265);
  --color-tlf20-ink: #1a2237;
  --color-tlf20-ink: oklch(0.25 0.04 265);
  --color-tlf20-mut: #4e5a76;
  --color-tlf20-mut: oklch(0.45 0.035 265);
  --color-tlf20-ph: #949db5;
  --color-tlf20-ph: oklch(0.67 0.025 265);
  --color-tlf20-acc: #3e59c9;
  --color-tlf20-acc: oklch(0.5 0.18 265);
  --color-tlf20-halo: #bec8f0;
  --color-tlf20-halo: oklch(0.5 0.18 265 / 0.16);
  --color-tlf20-ring: #93a2ea;
  --color-tlf20-ring: oklch(0.5 0.18 265 / 0.5);
  --font-tlf20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

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

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-tlf20-stage: #eef1f7;
  --color-tlf20-stage: oklch(0.95 0.008 265);
  --color-tlf20-edge: #e5e9f2;
  --color-tlf20-edge: oklch(0.92 0.008 265);
  --color-tlf20-fieldedge: #d8deeb;
  --color-tlf20-fieldedge: oklch(0.88 0.012 265);
  --color-tlf20-field: #fafbfe;
  --color-tlf20-field: oklch(0.99 0.003 265);
  --color-tlf20-hover: #f2f5fb;
  --color-tlf20-hover: oklch(0.97 0.006 265);
  --color-tlf20-track: #e6eaf3;
  --color-tlf20-track: oklch(0.93 0.008 265);
  --color-tlf20-ink: #1a2237;
  --color-tlf20-ink: oklch(0.25 0.04 265);
  --color-tlf20-mut: #4e5a76;
  --color-tlf20-mut: oklch(0.45 0.035 265);
  --color-tlf20-ph: #949db5;
  --color-tlf20-ph: oklch(0.67 0.025 265);
  --color-tlf20-acc: #3e59c9;
  --color-tlf20-acc: oklch(0.5 0.18 265);
  --color-tlf20-halo: #bec8f0;
  --color-tlf20-halo: oklch(0.5 0.18 265 / 0.16);
  --color-tlf20-ring: #93a2ea;
  --color-tlf20-ring: oklch(0.5 0.18 265 / 0.5);
  --font-tlf20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tlf-20 {
  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=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">

What changed in the port

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

  • Named peers (peer/s1, peer/s2, peer/s3) are what make this port readable: the progress fill's class list literally says "one third, two thirds at step 2, full at step 3", where the CSS version needed :nth-of-type() arithmetic to say the same thing.
  • -translate-x-1/3 and -translate-x-2/3 are exact fractions rather than the original's 33.333%, which removes a rounding seam that was visible at certain widths.
  • The inert caveat is real and worth stating rather than hiding: CSS-only wizards leave off-screen fields in the tab order, and that is the pattern's cost.
  • UI change from the CSS original: Plus Jakarta Sans replaces the system stack, the radios gained aria-labels that announce "Step 2 of 3", Back buttons went to 48px with a real hover, the muted Back ink darkened from #54607c to oklch(0.45 0.035 265), and the OTP field tracks its digits at 0.2em.
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 Multi-Step Login Wizard with Radio-Driven Track
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-multi-step-login-wizard-radio-track/

A multi-stage sign-in that lowers upfront resistance: email, then password, then a security code, with inactive panels sliding off a translated track. Three radios hold the stage, Next/Back are their labels, and the port's interesting constraint is that Tailwind's peer only reaches later siblings &mdash; which is exactly how this markup is already arranged.
## HTML
```html
<section class="tlf-20 w-full min-h-svh grid place-items-center bg-tlf20-stage font-tlf20-sans p-6">
  <form novalidate class="w-[min(380px,100%)] p-7 bg-white border border-tlf20-edge rounded-[18px] overflow-hidden shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.5)]">
    <input type="radio" name="tlf-20-step" id="tlf-20-s1" checked class="peer/s1 sr-only" aria-label="Step 1 of 3: email">
    <input type="radio" name="tlf-20-step" id="tlf-20-s2" class="peer/s2 sr-only" aria-label="Step 2 of 3: password">
    <input type="radio" name="tlf-20-step" id="tlf-20-s3" class="peer/s3 sr-only" aria-label="Step 3 of 3: security code">
    <div aria-hidden="true" class="h-1.5 mb-6 rounded-full bg-tlf20-track overflow-hidden">
      <span class="block h-full w-1/3 rounded-full bg-tlf20-acc transition-[width] duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked/s2:w-2/3 peer-checked/s3:w-full motion-reduce:transition-none"></span>
    </div>
    <div class="overflow-hidden">
      <div class="flex w-[300%] transition-transform duration-450 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked/s2:-translate-x-1/3 peer-checked/s3:-translate-x-2/3 motion-reduce:transition-none">
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Your email</legend>
          <input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <label for="tlf-20-s2" class="grid place-items-center h-12 rounded-xl cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter] duration-150 hover:brightness-110">Continue</label>
        </fieldset>
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Your password</legend>
          <input type="password" autocomplete="current-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <div class="flex gap-2.5">
            <label for="tlf-20-s1" class="grid place-items-center basis-[92px] h-12 rounded-xl cursor-pointer bg-white border-[1.5px] border-tlf20-fieldedge text-tlf20-mut text-[15px] font-semibold transition-colors duration-150 hover:bg-tlf20-hover">Back</label>
            <label for="tlf-20-s3" class="grid place-items-center flex-1 h-12 rounded-xl cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter] duration-150 hover:brightness-110">Continue</label>
          </div>
        </fieldset>
        <fieldset class="w-1/3 px-0.5 border-0 flex flex-col gap-3.5">
          <legend class="mb-1 p-0 text-[19px] font-bold text-tlf20-ink">Security code</legend>
          <input type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="6-digit code" required class="h-12 px-3.5 rounded-xl text-[15px] tracking-[0.2em] text-tlf20-ink bg-tlf20-field border-[1.5px] border-tlf20-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:tracking-normal placeholder:text-tlf20-ph focus-visible:outline-none focus-visible:border-tlf20-acc focus-visible:ring-4 focus-visible:ring-tlf20-halo">
          <div class="flex gap-2.5">
            <label for="tlf-20-s2" class="grid place-items-center basis-[92px] h-12 rounded-xl cursor-pointer bg-white border-[1.5px] border-tlf20-fieldedge text-tlf20-mut text-[15px] font-semibold transition-colors duration-150 hover:bg-tlf20-hover">Back</label>
            <button type="submit" class="flex-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf20-acc text-white text-[15px] font-semibold transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf20-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
          </div>
        </fieldset>
      </div>
    </div>
  </form>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tlf20-stage: #eef1f7;
  --color-tlf20-stage: oklch(0.95 0.008 265);
  --color-tlf20-edge: #e5e9f2;
  --color-tlf20-edge: oklch(0.92 0.008 265);
  --color-tlf20-fieldedge: #d8deeb;
  --color-tlf20-fieldedge: oklch(0.88 0.012 265);
  --color-tlf20-field: #fafbfe;
  --color-tlf20-field: oklch(0.99 0.003 265);
  --color-tlf20-hover: #f2f5fb;
  --color-tlf20-hover: oklch(0.97 0.006 265);
  --color-tlf20-track: #e6eaf3;
  --color-tlf20-track: oklch(0.93 0.008 265);
  --color-tlf20-ink: #1a2237;
  --color-tlf20-ink: oklch(0.25 0.04 265);
  --color-tlf20-mut: #4e5a76;
  --color-tlf20-mut: oklch(0.45 0.035 265);
  --color-tlf20-ph: #949db5;
  --color-tlf20-ph: oklch(0.67 0.025 265);
  --color-tlf20-acc: #3e59c9;
  --color-tlf20-acc: oklch(0.5 0.18 265);
  --color-tlf20-halo: #bec8f0;
  --color-tlf20-halo: oklch(0.5 0.18 265 / 0.16);
  --color-tlf20-ring: #93a2ea;
  --color-tlf20-ring: oklch(0.5 0.18 265 / 0.5);
  --font-tlf20-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

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

How this works

Radios, not checkboxes. Exactly one stage can be active, which is what a radio group means. Three sr-only radios sit first in the form; the progress bar and the track are later siblings, so every peer-checked/sN: variant resolves.

The track translates, nothing hides. w-[300%] with three w-1/3 panels, shifted by thirds. No display:none anywhere, so tab order is preserved and the browser never has to re-layout the card between steps.

Next and Back are labels. Each one points at the radio for its stage, so the whole wizard runs on keyboard with no script — and because the radios are real form controls, arrow keys move between stages too.

Make it yours

  • Add a stage: a fourth radio, w-[400%], w-1/4 panels and quarter translates.
  • Slide feel: duration-450 and the shared cubic-bezier.
  • Accent: --color-tlf20-acc drives progress, buttons and rings.
  • Replace the step content with your real fields — the track does not care what is inside.

Gotchas — read before shipping

  • Panels must be overflow-hidden's children, not hidden. Toggling display kills the animation and moves focus unpredictably.
  • Off-screen steps remain focusable — a production wizard should add inert to inactive panels, which CSS cannot do.
  • peer-checked/s2: requires the named peer (peer/s2) on the radio; unnamed peers all resolve to the nearest preceding .peer, which silently makes steps 2 and 3 identical.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Radio :checked sibling state and transform transitions are universally supported; the floor is Tailwind v4's generated sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…