15 / 30
All 30 in 30 Tailwind CSS Login Forms
- 01Tailwind Split-Screen Brand Hero Login Form
- 02Tailwind Multi-Tenant Workspace Account Switcher
- 03Tailwind Floating Label Login Form with peer Variants
- 04Tailwind Web3 Wallet Connect Login Grid
- 05Tailwind Minimalist Fintech Login Card with Layered Shadow
- 06Tailwind OAuth Social Login Form with Brand SVG Buttons
- 07Tailwind Passwordless Magic Link Login with data-state Machine
- 08Tailwind Aurora Glow Login Card with Drifting Blur Blobs
- 09Tailwind Neon Synthwave Arcade Login Form
- 10Tailwind Sliding Login and Sign Up Panel with peer-checked
- 11Tailwind 3D Flip Login Card with Password Reset Back Face
- 12Tailwind Terminal CLI Prompt Login Form
- 13Tailwind Floating Pill Login Widget Docked Over App Content
- 14Tailwind Gradient Border Login Card with background-clip
- 15Tailwind Slide-Over Side Panel Login without JavaScript
- 16Tailwind Social-First Sign Up with Brand-Compliant Buttons
- 17Tailwind Animated Mascot Login that Covers Its Eyes
- 18Tailwind Accessible Compact Login Modal with Scale-In
- 19Tailwind Glassmorphism Login Card with backdrop-filter
- 20Tailwind Multi-Step Login Wizard with Radio-Driven Track
- 21Tailwind Animated Conic Gradient Border Login Card
- 22Tailwind Secure Vault Login with user-invalid Error State
- 23Tailwind Neo-Brutalist Login Form with Hard Offset Shadow
- 24Tailwind Show Hide Password Toggle with aria-pressed
- 25Tailwind OTP Code Input Grid with Auto-Advance
- 26Tailwind Password Strength Meter with data-score Variants
- 27Tailwind Numeric PIN Pad Login for Kiosks and POS
- 28Tailwind Inline Form Validation with user-valid and user-invalid
- 29Tailwind Particle Constellation Login over Canvas
- 30Tailwind Biometric Face ID Prompt with Pulse Ripples
Tailwind Slide-Over Side Panel Login without JavaScript
Log in as a slide-over: the panel glides in from the right over a dimmed backdrop without routing away from the storefront — the e-commerce and content-hub pattern. One checkbox, three peer-checked: class lists, zero JavaScript, and the closed state correctly stops intercepting clicks.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tlf-15 relative w-full min-h-svh overflow-hidden bg-tlf15-stage font-tlf15-sans">
<input type="checkbox" id="tlf-15-open" aria-label="Open sign-in panel" class="peer sr-only">
<div class="w-full min-h-svh p-7">
<div class="flex items-center justify-between">
<span class="text-[17px] font-bold text-tlf15-ink">◈ Marketplace</span>
<label for="tlf-15-open" class="grid place-items-center min-h-11 px-5 rounded-full cursor-pointer bg-tlf15-acc text-white text-sm font-semibold transition-[filter] duration-150 hover:brightness-110">Sign in</label>
</div>
<p aria-hidden="true" class="mt-[38vh] text-center text-sm text-tlf15-mut">Your storefront stays put behind the panel.</p>
</div>
<label for="tlf-15-open" aria-label="Close sign-in panel" class="absolute inset-0 z-5 cursor-pointer bg-tlf15-scrim opacity-0 pointer-events-none transition-opacity duration-350 peer-checked:opacity-100 peer-checked:pointer-events-auto motion-reduce:transition-none"></label>
<div role="dialog" aria-label="Sign in" class="absolute top-0 right-0 z-6 h-full w-[min(380px,92%)] p-8 bg-white shadow-[-20px_0_60px_-30px_oklch(0.3_0.05_165/0.55)] translate-x-full transition-transform duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked:translate-x-0 peer-focus-visible:outline-3 peer-focus-visible:-outline-offset-4 peer-focus-visible:outline-tlf15-acc motion-reduce:transition-none">
<label for="tlf-15-open" aria-label="Close" class="absolute top-3 right-3 grid place-items-center size-11 rounded-lg cursor-pointer text-2xl leading-none text-tlf15-mut transition-colors duration-150 hover:bg-tlf15-hover hover:text-tlf15-ink">×</label>
<form novalidate class="mt-8 flex flex-col gap-3.5">
<h2 class="text-[23px] font-bold text-tlf15-ink">Welcome back</h2>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf15-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-tlf15-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-[12.5px] text-tlf15-mut">Guest checkout is always available.</p>
</form>
</div>
</section><section class="tlf-15 relative w-full min-h-svh overflow-hidden bg-tlf15-stage font-tlf15-sans">
<input type="checkbox" id="tlf-15-open" aria-label="Open sign-in panel" class="peer sr-only">
<div class="w-full min-h-svh p-7">
<div class="flex items-center justify-between">
<span class="text-[17px] font-bold text-tlf15-ink">◈ Marketplace</span>
<label for="tlf-15-open" class="grid place-items-center min-h-11 px-5 rounded-full cursor-pointer bg-tlf15-acc text-white text-sm font-semibold transition-[filter] duration-150 hover:brightness-110">Sign in</label>
</div>
<p aria-hidden="true" class="mt-[38vh] text-center text-sm text-tlf15-mut">Your storefront stays put behind the panel.</p>
</div>
<label for="tlf-15-open" aria-label="Close sign-in panel" class="absolute inset-0 z-5 cursor-pointer bg-tlf15-scrim opacity-0 pointer-events-none transition-opacity duration-350 peer-checked:opacity-100 peer-checked:pointer-events-auto motion-reduce:transition-none"></label>
<div role="dialog" aria-label="Sign in" class="absolute top-0 right-0 z-6 h-full w-[min(380px,92%)] p-8 bg-white shadow-[-20px_0_60px_-30px_oklch(0.3_0.05_165/0.55)] translate-x-full transition-transform duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked:translate-x-0 peer-focus-visible:outline-3 peer-focus-visible:-outline-offset-4 peer-focus-visible:outline-tlf15-acc motion-reduce:transition-none">
<label for="tlf-15-open" aria-label="Close" class="absolute top-3 right-3 grid place-items-center size-11 rounded-lg cursor-pointer text-2xl leading-none text-tlf15-mut transition-colors duration-150 hover:bg-tlf15-hover hover:text-tlf15-ink">×</label>
<form novalidate class="mt-8 flex flex-col gap-3.5">
<h2 class="text-[23px] font-bold text-tlf15-ink">Welcome back</h2>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf15-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-tlf15-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-[12.5px] text-tlf15-mut">Guest checkout is always available.</p>
</form>
</div>
</section>@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}<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=Be+Vietnam+Pro:wght@400;500;600;700&display=swap">
<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=Be+Vietnam+Pro:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Three elements react to one checkbox and each says so in its own class list — the clearest case in the collection for variants over selectors, because the CSS version's
~chains gave no hint from the panel's side that it was driven by an input elsewhere. z-5/z-6(bare numerics, v4) match the original's stacking exactly rather than rounding toz-10/z-20and inventing space that isn't used.- The honest caveat in
gotchasmatters: this pattern is right for a marketing drawer and wrong for a checkout-blocking modal, and no amount of utilities changes that. - UI change from the CSS original: Be Vietnam Pro replaces the system stack, the accent green deepened to
oklch(0.5 0.12 165)for white-text contrast, the close button went from 34px tosize-11, the muted hint text from#8ba296(2.9:1) tooklch(0.53 0.03 165)(4.9:1), and a guest-checkout line was added.
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 Slide-Over Side Panel Login without JavaScript
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-slide-over-side-panel-login/
Log in as a slide-over: the panel glides in from the right over a dimmed backdrop without routing away from the storefront — the e-commerce and content-hub pattern. One checkbox, three peer-checked: class lists, zero JavaScript, and the closed state correctly stops intercepting clicks.
## HTML
```html
<section class="tlf-15 relative w-full min-h-svh overflow-hidden bg-tlf15-stage font-tlf15-sans">
<input type="checkbox" id="tlf-15-open" aria-label="Open sign-in panel" class="peer sr-only">
<div class="w-full min-h-svh p-7">
<div class="flex items-center justify-between">
<span class="text-[17px] font-bold text-tlf15-ink">◈ Marketplace</span>
<label for="tlf-15-open" class="grid place-items-center min-h-11 px-5 rounded-full cursor-pointer bg-tlf15-acc text-white text-sm font-semibold transition-[filter] duration-150 hover:brightness-110">Sign in</label>
</div>
<p aria-hidden="true" class="mt-[38vh] text-center text-sm text-tlf15-mut">Your storefront stays put behind the panel.</p>
</div>
<label for="tlf-15-open" aria-label="Close sign-in panel" class="absolute inset-0 z-5 cursor-pointer bg-tlf15-scrim opacity-0 pointer-events-none transition-opacity duration-350 peer-checked:opacity-100 peer-checked:pointer-events-auto motion-reduce:transition-none"></label>
<div role="dialog" aria-label="Sign in" class="absolute top-0 right-0 z-6 h-full w-[min(380px,92%)] p-8 bg-white shadow-[-20px_0_60px_-30px_oklch(0.3_0.05_165/0.55)] translate-x-full transition-transform duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked:translate-x-0 peer-focus-visible:outline-3 peer-focus-visible:-outline-offset-4 peer-focus-visible:outline-tlf15-acc motion-reduce:transition-none">
<label for="tlf-15-open" aria-label="Close" class="absolute top-3 right-3 grid place-items-center size-11 rounded-lg cursor-pointer text-2xl leading-none text-tlf15-mut transition-colors duration-150 hover:bg-tlf15-hover hover:text-tlf15-ink">×</label>
<form novalidate class="mt-8 flex flex-col gap-3.5">
<h2 class="text-[23px] font-bold text-tlf15-ink">Welcome back</h2>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf15-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-tlf15-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-[12.5px] text-tlf15-mut">Guest checkout is always available.</p>
</form>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}
```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 Slide-Over Side Panel Login without JavaScript
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-slide-over-side-panel-login/
Log in as a slide-over: the panel glides in from the right over a dimmed backdrop without routing away from the storefront — the e-commerce and content-hub pattern. One checkbox, three peer-checked: class lists, zero JavaScript, and the closed state correctly stops intercepting clicks.
## HTML
```html
<section class="tlf-15 relative w-full min-h-svh overflow-hidden bg-tlf15-stage font-tlf15-sans">
<input type="checkbox" id="tlf-15-open" aria-label="Open sign-in panel" class="peer sr-only">
<div class="w-full min-h-svh p-7">
<div class="flex items-center justify-between">
<span class="text-[17px] font-bold text-tlf15-ink">◈ Marketplace</span>
<label for="tlf-15-open" class="grid place-items-center min-h-11 px-5 rounded-full cursor-pointer bg-tlf15-acc text-white text-sm font-semibold transition-[filter] duration-150 hover:brightness-110">Sign in</label>
</div>
<p aria-hidden="true" class="mt-[38vh] text-center text-sm text-tlf15-mut">Your storefront stays put behind the panel.</p>
</div>
<label for="tlf-15-open" aria-label="Close sign-in panel" class="absolute inset-0 z-5 cursor-pointer bg-tlf15-scrim opacity-0 pointer-events-none transition-opacity duration-350 peer-checked:opacity-100 peer-checked:pointer-events-auto motion-reduce:transition-none"></label>
<div role="dialog" aria-label="Sign in" class="absolute top-0 right-0 z-6 h-full w-[min(380px,92%)] p-8 bg-white shadow-[-20px_0_60px_-30px_oklch(0.3_0.05_165/0.55)] translate-x-full transition-transform duration-400 ease-[cubic-bezier(0.4,0,0.2,1)] peer-checked:translate-x-0 peer-focus-visible:outline-3 peer-focus-visible:-outline-offset-4 peer-focus-visible:outline-tlf15-acc motion-reduce:transition-none">
<label for="tlf-15-open" aria-label="Close" class="absolute top-3 right-3 grid place-items-center size-11 rounded-lg cursor-pointer text-2xl leading-none text-tlf15-mut transition-colors duration-150 hover:bg-tlf15-hover hover:text-tlf15-ink">×</label>
<form novalidate class="mt-8 flex flex-col gap-3.5">
<h2 class="text-[23px] font-bold text-tlf15-ink">Welcome back</h2>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@company.com" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf15-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-xl text-[15px] text-tlf15-ink bg-tlf15-field border-[1.5px] border-tlf15-edge transition-[border-color,box-shadow] duration-150 placeholder:text-tlf15-ph focus-visible:outline-none focus-visible:border-tlf15-acc focus-visible:ring-4 focus-visible:ring-tlf15-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-xl border-0 cursor-pointer bg-tlf15-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-tlf15-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-[12.5px] text-tlf15-mut">Guest checkout is always available.</p>
</form>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf15-stage: #f2f5f3;
--color-tlf15-stage: oklch(0.96 0.008 165);
--color-tlf15-ink: #173a2c;
--color-tlf15-ink: oklch(0.31 0.05 165);
--color-tlf15-label: #33574a;
--color-tlf15-label: oklch(0.42 0.04 165);
--color-tlf15-mut: #5d7a6d;
--color-tlf15-mut: oklch(0.53 0.03 165);
--color-tlf15-ph: #8aa79a;
--color-tlf15-ph: oklch(0.67 0.03 165);
--color-tlf15-edge: #d3ded7;
--color-tlf15-edge: oklch(0.88 0.012 165);
--color-tlf15-field: #fbfdfc;
--color-tlf15-field: oklch(0.99 0.004 165);
--color-tlf15-hover: #eef2ef;
--color-tlf15-hover: oklch(0.95 0.008 165);
--color-tlf15-acc: #0f7a5a;
--color-tlf15-acc: oklch(0.5 0.12 165);
--color-tlf15-halo: #a8d9c7;
--color-tlf15-halo: oklch(0.5 0.12 165 / 0.18);
--color-tlf15-ring: #74c2a8;
--color-tlf15-ring: oklch(0.5 0.12 165 / 0.5);
--color-tlf15-scrim: #0a1e14;
--color-tlf15-scrim: oklch(0.2 0.03 165 / 0.45);
--font-tlf15-sans: 'Be Vietnam Pro', system-ui, sans-serif;
}
.tlf-15 {
width: 100%;
min-height: 100svh;
}
```How this works
Two labels, one checkbox. The nav trigger, the close button and the backdrop are all <label for="tlf-15-open">. The checkbox is peer sr-only, so it stays focusable and Space toggles the drawer — a pattern that gives keyboard parity without a line of script.
The closed backdrop must not block the page. opacity-0 pointer-events-none at rest, both flipped by peer-checked:. Forgetting the pointer-events half is the classic version of this bug: an invisible overlay that eats every click on the storefront underneath.
Transform in, opacity for the scrim. The panel never animates right, so the drawer slide is a compositor operation even on a long product page.
Make it yours
- Slide from the left:
left-0 -translate-x-fulland the samepeer-checked:translate-x-0. - Panel width:
w-[min(380px,92%)]. - Speed:
duration-400. - Accent:
--color-tlf15-accdrives the trigger, the button and both focus rings.
Gotchas — read before shipping
- A real product would also need focus trapping and Escape-to-close, which CSS cannot do — this is the pattern's honest limit, and the reason to reach for
<dialog>when the drawer is load-bearing. - Pair every
opacity-0overlay withpointer-events-none. peerrequires the checkbox to precede the panel and backdrop as a sibling; nesting it inside the nav breaks every variant silently.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:checked sibling state and transform transitions are universally supported; the floor is Tailwind v4's generated sheet.