30 Tailwind CSS Login Forms
24 / 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 Show Hide Password Toggle with aria-pressed

The everyday accessibility win: an eye button inside the password field flips it between hidden and visible so users can catch typos. The port's move is styling from the accessibility attribute — aria-[pressed=true]: draws the strike-through on the eye, so the visual state and the announced state literally cannot diverge.

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-24 w-full min-h-svh grid place-items-center bg-tlf24-stage font-tlf24-sans p-6">
  <form novalidate class="w-[min(380px,100%)] flex flex-col gap-3.5 p-8 bg-white border border-tlf24-edge rounded-2xl shadow-[0_22px_50px_-38px_oklch(0.25_0.06_265/0.5)]">
    <h1 class="text-[23px] font-bold text-tlf24-ink">Log in</h1>
    <label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf24-label">Email</span>
      <input type="email" autocomplete="email" placeholder="you@company.com" required class="h-[50px] px-3.5 rounded-xl text-[15px] text-tlf24-ink bg-tlf24-field border-[1.5px] border-tlf24-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf24-ph focus-visible:outline-none focus-visible:border-tlf24-acc focus-visible:ring-4 focus-visible:ring-tlf24-halo"></label>
    <div class="flex flex-col gap-1.5">
      <label for="tlf-24-pw" class="text-[13px] font-semibold text-tlf24-label">Password</label>
      <div class="relative">
        <input id="tlf-24-pw" class="tlf-24-pw w-full h-[50px] pl-3.5 pr-14 rounded-xl text-[15px] text-tlf24-ink bg-tlf24-field border-[1.5px] border-tlf24-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf24-ph focus-visible:outline-none focus-visible:border-tlf24-acc focus-visible:ring-4 focus-visible:ring-tlf24-halo" type="password" autocomplete="current-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" required>
        <button type="button" aria-pressed="false" aria-label="Show password" class="tlf-24-eye group absolute right-1.5 top-1/2 -translate-y-1/2 grid place-items-center size-11 rounded-[10px] border-0 cursor-pointer bg-transparent text-tlf24-mut transition-colors duration-150 hover:bg-tlf24-stage hover:text-tlf24-acc focus-visible:outline-3 focus-visible:outline-offset-1 focus-visible:outline-tlf24-ring aria-[pressed=true]:text-tlf24-acc">
          <span aria-hidden="true" class="relative block w-[22px] h-3.5 border-2 border-current rounded-[50%/60%] after:content-[''] after:absolute after:left-1/2 after:top-1/2 after:size-1.5 after:-translate-x-1/2 after:-translate-y-1/2 after:rounded-full after:bg-current before:content-[''] before:absolute before:-left-[3px] before:top-[5px] before:w-7 before:h-0.5 before:bg-current before:origin-center before:rotate-[-30deg] before:opacity-0 before:transition-opacity before:duration-150 group-aria-[pressed=true]:before:opacity-100"></span>
        </button>
      </div>
    </div>
    <button type="submit" class="mt-1 h-[50px] rounded-xl border-0 cursor-pointer bg-tlf24-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-tlf24-ring motion-reduce:transition-none motion-reduce:active:scale-100">Continue</button>
  </form>
</section>
@import "tailwindcss";

@theme {
  --color-tlf24-stage: #eef1f7;
  --color-tlf24-stage: oklch(0.95 0.008 260);
  --color-tlf24-edge: #e5e9f2;
  --color-tlf24-edge: oklch(0.92 0.008 260);
  --color-tlf24-fieldedge: #d8deeb;
  --color-tlf24-fieldedge: oklch(0.88 0.012 260);
  --color-tlf24-field: #fafbfe;
  --color-tlf24-field: oklch(0.99 0.003 260);
  --color-tlf24-ink: #1a2237;
  --color-tlf24-ink: oklch(0.25 0.04 260);
  --color-tlf24-label: #3d465f;
  --color-tlf24-label: oklch(0.37 0.035 260);
  --color-tlf24-mut: #5f6884;
  --color-tlf24-mut: oklch(0.5 0.03 260);
  --color-tlf24-ph: #949db5;
  --color-tlf24-ph: oklch(0.67 0.025 260);
  --color-tlf24-acc: #3b63c4;
  --color-tlf24-acc: oklch(0.5 0.16 258);
  --color-tlf24-halo: #bfcdee;
  --color-tlf24-halo: oklch(0.5 0.16 258 / 0.16);
  --color-tlf24-ring: #94a8e6;
  --color-tlf24-ring: oklch(0.5 0.16 258 / 0.5);
  --font-tlf24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tlf-24 {
  width: 100%;
  min-height: 100svh;
}
const section = document.querySelector('.tlf-24');
const btn = section.querySelector('.tlf-24-eye');
const pw = section.querySelector('.tlf-24-pw');

btn.addEventListener('click', () => {
  const show = pw.type === 'password';
  pw.type = show ? 'text' : 'password';
  btn.setAttribute('aria-pressed', String(show));
  btn.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
  pw.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-tlf24-stage: #eef1f7;
  --color-tlf24-stage: oklch(0.95 0.008 260);
  --color-tlf24-edge: #e5e9f2;
  --color-tlf24-edge: oklch(0.92 0.008 260);
  --color-tlf24-fieldedge: #d8deeb;
  --color-tlf24-fieldedge: oklch(0.88 0.012 260);
  --color-tlf24-field: #fafbfe;
  --color-tlf24-field: oklch(0.99 0.003 260);
  --color-tlf24-ink: #1a2237;
  --color-tlf24-ink: oklch(0.25 0.04 260);
  --color-tlf24-label: #3d465f;
  --color-tlf24-label: oklch(0.37 0.035 260);
  --color-tlf24-mut: #5f6884;
  --color-tlf24-mut: oklch(0.5 0.03 260);
  --color-tlf24-ph: #949db5;
  --color-tlf24-ph: oklch(0.67 0.025 260);
  --color-tlf24-acc: #3b63c4;
  --color-tlf24-acc: oklch(0.5 0.16 258);
  --color-tlf24-halo: #bfcdee;
  --color-tlf24-halo: oklch(0.5 0.16 258 / 0.16);
  --color-tlf24-ring: #94a8e6;
  --color-tlf24-ring: oklch(0.5 0.16 258 / 0.5);
  --font-tlf24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.tlf-24 {
  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&display=swap">

What changed in the port

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

  • group-aria-[pressed=true]:before:opacity-100 is the whole port: the slash on the eye is drawn by the same attribute a screen reader reads, so a future refactor cannot leave the icon lying about the state.
  • The eye is CSS-drawn with rounded-[50%/60%] — a two-radius value that Tailwind passes through intact, and a good reminder that arbitrary values accept the full CSS grammar.
  • pr-14 and size-11 are a matched pair; noting that in customization rather than leaving it implicit is the difference between a snippet and a component.
  • UI change from the CSS original: Plus Jakarta Sans replaces the system stack, the toggle grew from 36px to size-11 (44px) with the field padding adjusted to match, the muted icon colour darkened from #8a91a8 to oklch(0.5 0.03 260), and the accent deepened for white-text contrast.
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 Show Hide Password Toggle with aria-pressed
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-show-hide-password-toggle-aria-pressed/

The everyday accessibility win: an eye button inside the password field flips it between hidden and visible so users can catch typos. The port's move is styling from the accessibility attribute &mdash; aria-[pressed=true]: draws the strike-through on the eye, so the visual state and the announced state literally cannot diverge.
## HTML
```html
<section class="tlf-24 w-full min-h-svh grid place-items-center bg-tlf24-stage font-tlf24-sans p-6">
  <form novalidate class="w-[min(380px,100%)] flex flex-col gap-3.5 p-8 bg-white border border-tlf24-edge rounded-2xl shadow-[0_22px_50px_-38px_oklch(0.25_0.06_265/0.5)]">
    <h1 class="text-[23px] font-bold text-tlf24-ink">Log in</h1>
    <label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf24-label">Email</span>
      <input type="email" autocomplete="email" placeholder="you@company.com" required class="h-[50px] px-3.5 rounded-xl text-[15px] text-tlf24-ink bg-tlf24-field border-[1.5px] border-tlf24-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf24-ph focus-visible:outline-none focus-visible:border-tlf24-acc focus-visible:ring-4 focus-visible:ring-tlf24-halo"></label>
    <div class="flex flex-col gap-1.5">
      <label for="tlf-24-pw" class="text-[13px] font-semibold text-tlf24-label">Password</label>
      <div class="relative">
        <input id="tlf-24-pw" class="tlf-24-pw w-full h-[50px] pl-3.5 pr-14 rounded-xl text-[15px] text-tlf24-ink bg-tlf24-field border-[1.5px] border-tlf24-fieldedge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf24-ph focus-visible:outline-none focus-visible:border-tlf24-acc focus-visible:ring-4 focus-visible:ring-tlf24-halo" type="password" autocomplete="current-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" required>
        <button type="button" aria-pressed="false" aria-label="Show password" class="tlf-24-eye group absolute right-1.5 top-1/2 -translate-y-1/2 grid place-items-center size-11 rounded-[10px] border-0 cursor-pointer bg-transparent text-tlf24-mut transition-colors duration-150 hover:bg-tlf24-stage hover:text-tlf24-acc focus-visible:outline-3 focus-visible:outline-offset-1 focus-visible:outline-tlf24-ring aria-[pressed=true]:text-tlf24-acc">
          <span aria-hidden="true" class="relative block w-[22px] h-3.5 border-2 border-current rounded-[50%/60%] after:content-[''] after:absolute after:left-1/2 after:top-1/2 after:size-1.5 after:-translate-x-1/2 after:-translate-y-1/2 after:rounded-full after:bg-current before:content-[''] before:absolute before:-left-[3px] before:top-[5px] before:w-7 before:h-0.5 before:bg-current before:origin-center before:rotate-[-30deg] before:opacity-0 before:transition-opacity before:duration-150 group-aria-[pressed=true]:before:opacity-100"></span>
        </button>
      </div>
    </div>
    <button type="submit" class="mt-1 h-[50px] rounded-xl border-0 cursor-pointer bg-tlf24-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-tlf24-ring motion-reduce:transition-none motion-reduce:active:scale-100">Continue</button>
  </form>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tlf24-stage: #eef1f7;
  --color-tlf24-stage: oklch(0.95 0.008 260);
  --color-tlf24-edge: #e5e9f2;
  --color-tlf24-edge: oklch(0.92 0.008 260);
  --color-tlf24-fieldedge: #d8deeb;
  --color-tlf24-fieldedge: oklch(0.88 0.012 260);
  --color-tlf24-field: #fafbfe;
  --color-tlf24-field: oklch(0.99 0.003 260);
  --color-tlf24-ink: #1a2237;
  --color-tlf24-ink: oklch(0.25 0.04 260);
  --color-tlf24-label: #3d465f;
  --color-tlf24-label: oklch(0.37 0.035 260);
  --color-tlf24-mut: #5f6884;
  --color-tlf24-mut: oklch(0.5 0.03 260);
  --color-tlf24-ph: #949db5;
  --color-tlf24-ph: oklch(0.67 0.025 260);
  --color-tlf24-acc: #3b63c4;
  --color-tlf24-acc: oklch(0.5 0.16 258);
  --color-tlf24-halo: #bfcdee;
  --color-tlf24-halo: oklch(0.5 0.16 258 / 0.16);
  --color-tlf24-ring: #94a8e6;
  --color-tlf24-ring: oklch(0.5 0.16 258 / 0.5);
  --font-tlf24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

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

## JavaScript
```js
const section = document.querySelector('.tlf-24');
const btn = section.querySelector('.tlf-24-eye');
const pw = section.querySelector('.tlf-24-pw');

btn.addEventListener('click', () => {
  const show = pw.type === 'password';
  pw.type = show ? 'text' : 'password';
  btn.setAttribute('aria-pressed', String(show));
  btn.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
  pw.focus();
});
```

How this works

JS does the one thing CSS cannot. An input's type is not settable from CSS, so a four-line handler flips password↔text and updates aria-pressed and aria-label. Everything visual keys off that attribute, which means there is no second source of truth to keep in sync.

Reserve the space, don't overlap it. pr-14 on the field is the difference between a toggle and a bug report: without it a long password runs under the button and the last characters are unreadable at exactly the moment the user wanted to read them.

It stays a native password field. Type-flipping does not break password managers, and autocomplete="current-password" survives the switch — which is why this is the right implementation rather than a masked text field.

Make it yours

  • Accent: --color-tlf24-acc drives the hover tint, the ring and the field focus.
  • Swap the CSS eye for an inline SVG pair — keep aria-hidden on the art and the label on the button.
  • Button size: size-11 plus the matching pr-14 — change both.
  • Add a strength meter beneath (demo 26) using the same field.

Gotchas — read before shipping

  • Toggle aria-pressed AND aria-label. A glyph swap alone tells a screen-reader user nothing.
  • Keep the button type="button", or it submits the form on click.
  • Return focus to the input after the toggle (the handler does), otherwise keyboard users lose their place mid-password.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Attribute-selector variants and CSS-drawn glyphs are universally supported; the type flip needs the handler.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…