08 / 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 Aurora Glow Login Card with Drifting Blur Blobs
The indie-startup landing look: two big blurred colour fields drift behind a translucent login card like a slow aurora. High aesthetic impact, engineered to stay smooth on mobile — only transform animates, so the expensive blur is rasterised once and cheaply moved.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tlf-08 relative w-full min-h-svh grid place-items-center overflow-hidden bg-tlf08-stage font-tlf08-sans p-6">
<div aria-hidden="true" class="absolute -inset-[20%] z-0">
<div class="absolute left-[2%] top-[5%] w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-a bg-[radial-gradient(circle,oklch(0.7_0.2_300),transparent_70%)] motion-reduce:animate-none"></div>
<div class="absolute right-0 bottom-0 w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-b bg-[radial-gradient(circle,oklch(0.72_0.19_210),transparent_70%)] motion-reduce:animate-none"></div>
</div>
<form novalidate class="relative z-1 w-[min(380px,100%)] flex flex-col gap-4 p-8 rounded-[20px] bg-tlf08-card backdrop-blur-[6px] border border-white/10 text-tlf08-ink shadow-[0_30px_80px_-40px_black]">
<h1 class="text-[26px] font-bold tracking-tight">Welcome back</h1>
<p class="-mt-2.5 mb-1 text-sm text-tlf08-mut">Sign in to keep building.</p>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@studio.com" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-[14px] border-0 cursor-pointer text-white text-[15px] font-semibold bg-[linear-gradient(90deg,oklch(0.6_0.2_300),oklch(0.6_0.19_250))] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-tlf08-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-center text-[13px]"><a href="#" class="text-tlf08-link no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf08-link">Forgot password?</a></p>
</form>
</section><section class="tlf-08 relative w-full min-h-svh grid place-items-center overflow-hidden bg-tlf08-stage font-tlf08-sans p-6">
<div aria-hidden="true" class="absolute -inset-[20%] z-0">
<div class="absolute left-[2%] top-[5%] w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-a bg-[radial-gradient(circle,oklch(0.7_0.2_300),transparent_70%)] motion-reduce:animate-none"></div>
<div class="absolute right-0 bottom-0 w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-b bg-[radial-gradient(circle,oklch(0.72_0.19_210),transparent_70%)] motion-reduce:animate-none"></div>
</div>
<form novalidate class="relative z-1 w-[min(380px,100%)] flex flex-col gap-4 p-8 rounded-[20px] bg-tlf08-card backdrop-blur-[6px] border border-white/10 text-tlf08-ink shadow-[0_30px_80px_-40px_black]">
<h1 class="text-[26px] font-bold tracking-tight">Welcome back</h1>
<p class="-mt-2.5 mb-1 text-sm text-tlf08-mut">Sign in to keep building.</p>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@studio.com" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-[14px] border-0 cursor-pointer text-white text-[15px] font-semibold bg-[linear-gradient(90deg,oklch(0.6_0.2_300),oklch(0.6_0.19_250))] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-tlf08-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-center text-[13px]"><a href="#" class="text-tlf08-link no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf08-link">Forgot password?</a></p>
</form>
</section>@import "tailwindcss";
@theme {
--color-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}@import "tailwindcss";
@theme {
--color-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}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-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}@import "tailwindcss";
@theme {
--color-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}<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=Sora: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=Sora:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Two
@themeanimation entries are all the port needs; the@keyframesthemselves cannot be utilities and stay in the stylesheet, which is the standard division of labour for every animated demo in this collection. bg-white/5andborder-white/15replacergba(255,255,255,.04)literals — the slash-opacity syntax is the single most-used quality-of-life feature in a glass design.z-1(v4 allows bare numeric z-index) instead ofz-10: the original stacked at 0/1, and matching it exactly avoids inventing a stacking context the design does not need.- UI change from the CSS original: Sora replaces the system stack; card opacity went from 0.72 to 0.78 and the muted/placeholder greys lifted (
#a9adc4→oklch(0.78),#7f849e→0.65) so text stays AA at every frame of the aurora loop rather than most of them.
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 Aurora Glow Login Card with Drifting Blur Blobs
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-aurora-glow-login-card-blur-blobs/
The indie-startup landing look: two big blurred colour fields drift behind a translucent login card like a slow aurora. High aesthetic impact, engineered to stay smooth on mobile — only transform animates, so the expensive blur is rasterised once and cheaply moved.
## HTML
```html
<section class="tlf-08 relative w-full min-h-svh grid place-items-center overflow-hidden bg-tlf08-stage font-tlf08-sans p-6">
<div aria-hidden="true" class="absolute -inset-[20%] z-0">
<div class="absolute left-[2%] top-[5%] w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-a bg-[radial-gradient(circle,oklch(0.7_0.2_300),transparent_70%)] motion-reduce:animate-none"></div>
<div class="absolute right-0 bottom-0 w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-b bg-[radial-gradient(circle,oklch(0.72_0.19_210),transparent_70%)] motion-reduce:animate-none"></div>
</div>
<form novalidate class="relative z-1 w-[min(380px,100%)] flex flex-col gap-4 p-8 rounded-[20px] bg-tlf08-card backdrop-blur-[6px] border border-white/10 text-tlf08-ink shadow-[0_30px_80px_-40px_black]">
<h1 class="text-[26px] font-bold tracking-tight">Welcome back</h1>
<p class="-mt-2.5 mb-1 text-sm text-tlf08-mut">Sign in to keep building.</p>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@studio.com" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-[14px] border-0 cursor-pointer text-white text-[15px] font-semibold bg-[linear-gradient(90deg,oklch(0.6_0.2_300),oklch(0.6_0.19_250))] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-tlf08-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-center text-[13px]"><a href="#" class="text-tlf08-link no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf08-link">Forgot password?</a></p>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}
```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 Aurora Glow Login Card with Drifting Blur Blobs
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-aurora-glow-login-card-blur-blobs/
The indie-startup landing look: two big blurred colour fields drift behind a translucent login card like a slow aurora. High aesthetic impact, engineered to stay smooth on mobile — only transform animates, so the expensive blur is rasterised once and cheaply moved.
## HTML
```html
<section class="tlf-08 relative w-full min-h-svh grid place-items-center overflow-hidden bg-tlf08-stage font-tlf08-sans p-6">
<div aria-hidden="true" class="absolute -inset-[20%] z-0">
<div class="absolute left-[2%] top-[5%] w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-a bg-[radial-gradient(circle,oklch(0.7_0.2_300),transparent_70%)] motion-reduce:animate-none"></div>
<div class="absolute right-0 bottom-0 w-3/5 h-3/5 rounded-full opacity-85 blur-[90px] will-change-transform animate-tlf08-b bg-[radial-gradient(circle,oklch(0.72_0.19_210),transparent_70%)] motion-reduce:animate-none"></div>
</div>
<form novalidate class="relative z-1 w-[min(380px,100%)] flex flex-col gap-4 p-8 rounded-[20px] bg-tlf08-card backdrop-blur-[6px] border border-white/10 text-tlf08-ink shadow-[0_30px_80px_-40px_black]">
<h1 class="text-[26px] font-bold tracking-tight">Welcome back</h1>
<p class="-mt-2.5 mb-1 text-sm text-tlf08-mut">Sign in to keep building.</p>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Email</span>
<input type="email" autocomplete="email" placeholder="you@studio.com" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<label class="flex flex-col gap-1.5"><span class="text-[13px] font-semibold text-tlf08-label">Password</span>
<input type="password" autocomplete="current-password" placeholder="••••••••" required class="h-12 px-3.5 rounded-[14px] text-[15px] text-tlf08-ink bg-white/5 border-[1.5px] border-white/15 transition-[border-color,box-shadow] duration-150 placeholder:text-tlf08-ph focus-visible:outline-none focus-visible:border-tlf08-acc focus-visible:ring-4 focus-visible:ring-tlf08-halo"></label>
<button type="submit" class="mt-1 h-12 rounded-[14px] border-0 cursor-pointer text-white text-[15px] font-semibold bg-[linear-gradient(90deg,oklch(0.6_0.2_300),oklch(0.6_0.19_250))] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-[0.985] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-tlf08-ring motion-reduce:transition-none motion-reduce:active:scale-100">Sign in</button>
<p class="text-center text-[13px]"><a href="#" class="text-tlf08-link no-underline hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tlf08-link">Forgot password?</a></p>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf08-stage: #0a0a14;
--color-tlf08-stage: oklch(0.13 0.02 280);
--color-tlf08-card: #121222;
--color-tlf08-card: oklch(0.19 0.025 285 / 0.78);
--color-tlf08-ink: #eef0fa;
--color-tlf08-ink: oklch(0.96 0.008 280);
--color-tlf08-label: #cfd2e6;
--color-tlf08-label: oklch(0.87 0.014 280);
--color-tlf08-mut: #b3b7cc;
--color-tlf08-mut: oklch(0.78 0.018 280);
--color-tlf08-ph: #8d92ab;
--color-tlf08-ph: oklch(0.65 0.02 280);
--color-tlf08-acc: #b37cf0;
--color-tlf08-acc: oklch(0.7 0.19 300);
--color-tlf08-halo: #d7bcf7;
--color-tlf08-halo: oklch(0.7 0.19 300 / 0.28);
--color-tlf08-ring: #cba6f5;
--color-tlf08-ring: oklch(0.7 0.19 300 / 0.6);
--color-tlf08-link: #d3c6fb;
--color-tlf08-link: oklch(0.85 0.09 295);
--font-tlf08-sans: 'Sora', system-ui, sans-serif;
--animate-tlf08-a: tlf-08-a 16s ease-in-out infinite;
--animate-tlf08-b: tlf-08-b 20s ease-in-out infinite;
}
@keyframes tlf-08-a {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(30%, 20%) rotate(40deg);
}
}
@keyframes tlf-08-b {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(-25%, -15%) rotate(-30deg);
}
}
.tlf-08 {
width: 100%;
min-height: 100svh;
color-scheme: dark;
}
/* Safari < 16.4 backdrop-filter prefix. */
.tlf-08 form {
-webkit-backdrop-filter: blur(6px);
}
```How this works
The blobs became real elements. The CSS version drew them on ::before/::after of one background node. In Tailwind, two <div>s are cheaper to read and cost nothing extra: each carries its own gradient utility, its own animation utility and will-change-transform. Pseudo-element utilities are available (before:, after:) but a 60-character gradient inside before:bg-[...] is where the idiom stops paying.
Transform only, forever. The keyframes translate and rotate; the blur radius and the blob size never change. Animating filter: blur() re-rasterises a 500px-wide layer every frame, which is the single fastest way to turn a beautiful login screen into a 12fps one on a mid-range phone.
Contrast survives the glass. The card is a near-opaque dark surface with a hairline border-white/10; the aurora reads through as tint, not as noise behind body text.
Make it yours
- Recolour the aurora in the two gradient utilities — hue 300 and 210 here.
- Drift speed and path: the two
@keyframesblocks and the durations in the@themeanimation values. - Atmosphere:
blur-[90px]up for softer, down for defined shapes. - Glass level:
--color-tlf08-card's alpha, andbackdrop-blur-[6px].
Gotchas — read before shipping
- Animate
transformonly. This is the whole engineering content of the demo. will-change-transformon the blobs is not optional here — without it Chromium may keep re-rasterising the blurred layer.- Keep enough card opacity behind the text. A 30%-opaque card over a moving aurora fails AA at some point in every loop, and it is the frame you did not screenshot.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
filter: blur(), will-change and transform animations are universally supported; backdrop-filter needs the -webkit- prefix below Safari 16.4, which the stylesheet ships.