02 / 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 Multi-Tenant Workspace Account Switcher
The B2B enterprise front door (think Slack or AWS): a returning user picks which workspace to enter from a stack of profile rows, each with a tinted avatar and a role badge. The port keeps the original's best idea — one --tint hue per row drives avatar, badge, hover and ring — because a per-row hue is data, not a design-scale value, and belongs in a style attribute rather than four utilities.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tlf-02 w-full min-h-svh grid place-items-center bg-tlf02-stage font-tlf02-sans px-5 py-8">
<div class="w-[min(420px,100%)] p-6 bg-white border border-tlf02-edge rounded-[20px] shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.55)]">
<header class="mb-4">
<h1 class="text-xl font-bold tracking-tight text-tlf02-ink">Choose a workspace</h1>
<p class="mt-1 text-[13px] text-tlf02-mut">You’re signed in as ana@lumen.io</p>
</header>
<ul class="flex flex-col gap-2 list-none m-0 p-0">
<li style="--tint:264">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">LM</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Lumen Design</span>
<span class="text-[12.5px] text-tlf02-mut">lumen.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Owner</span>
</a>
</li>
<li style="--tint:160">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">AC</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Acme Corp</span>
<span class="text-[12.5px] text-tlf02-mut">acme.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Admin</span>
</a>
</li>
<li style="--tint:30">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">NW</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Northwind</span>
<span class="text-[12.5px] text-tlf02-mut">northwind.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Member</span>
</a>
</li>
</ul>
<a href="#" class="flex items-center justify-center gap-2 min-h-11 mt-4 p-3 rounded-xl border-[1.5px] border-dashed border-tlf02-dash text-sm font-semibold text-tlf02-mut no-underline transition-colors duration-150 hover:border-tlf02-acc hover:text-tlf02-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf02-ring"><span aria-hidden="true" class="text-[17px] leading-none">+</span> Add another account</a>
</div>
</section><section class="tlf-02 w-full min-h-svh grid place-items-center bg-tlf02-stage font-tlf02-sans px-5 py-8">
<div class="w-[min(420px,100%)] p-6 bg-white border border-tlf02-edge rounded-[20px] shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.55)]">
<header class="mb-4">
<h1 class="text-xl font-bold tracking-tight text-tlf02-ink">Choose a workspace</h1>
<p class="mt-1 text-[13px] text-tlf02-mut">You’re signed in as ana@lumen.io</p>
</header>
<ul class="flex flex-col gap-2 list-none m-0 p-0">
<li style="--tint:264">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">LM</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Lumen Design</span>
<span class="text-[12.5px] text-tlf02-mut">lumen.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Owner</span>
</a>
</li>
<li style="--tint:160">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">AC</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Acme Corp</span>
<span class="text-[12.5px] text-tlf02-mut">acme.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Admin</span>
</a>
</li>
<li style="--tint:30">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">NW</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Northwind</span>
<span class="text-[12.5px] text-tlf02-mut">northwind.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Member</span>
</a>
</li>
</ul>
<a href="#" class="flex items-center justify-center gap-2 min-h-11 mt-4 p-3 rounded-xl border-[1.5px] border-dashed border-tlf02-dash text-sm font-semibold text-tlf02-mut no-underline transition-colors duration-150 hover:border-tlf02-acc hover:text-tlf02-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf02-ring"><span aria-hidden="true" class="text-[17px] leading-none">+</span> Add another account</a>
</div>
</section>@import "tailwindcss";
@theme {
--color-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}@import "tailwindcss";
@theme {
--color-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}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-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}@import "tailwindcss";
@theme {
--color-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}<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">
<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.
- This is the demo that proves not everything should become a utility. Three rows × four tinted properties would be twelve arbitrary colour classes; one
--tinton the<li>plusvar(--tint)inside the utilities keeps it at one number per row, which is exactly what the CSS original did well. truncatereplaces the original's three-declarationwhite-space/overflow/text-overflowstack, andmin-w-0next to it is the non-obvious part: without it the flex child refuses to shrink and the badge leaves the card.- The
@supports not (background: color-mix(...))block is one of the few things utilities cannot express — feature queries have no variant form, so it stays in the stylesheet. - UI change from the CSS original: rows gained
min-h-11(44px), the badge text darkened tooklch(0.42)for 4.5:1 on its own 16% wash, and the muted email colour lifted from#8a91aatooklch(0.51 0.03 270).
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-Tenant Workspace Account Switcher
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-multi-tenant-workspace-account-switcher/
The B2B enterprise front door (think Slack or AWS): a returning user picks which workspace to enter from a stack of profile rows, each with a tinted avatar and a role badge. The port keeps the original's best idea — one --tint hue per row drives avatar, badge, hover and ring — because a per-row hue is data, not a design-scale value, and belongs in a style attribute rather than four utilities.
## HTML
```html
<section class="tlf-02 w-full min-h-svh grid place-items-center bg-tlf02-stage font-tlf02-sans px-5 py-8">
<div class="w-[min(420px,100%)] p-6 bg-white border border-tlf02-edge rounded-[20px] shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.55)]">
<header class="mb-4">
<h1 class="text-xl font-bold tracking-tight text-tlf02-ink">Choose a workspace</h1>
<p class="mt-1 text-[13px] text-tlf02-mut">You’re signed in as ana@lumen.io</p>
</header>
<ul class="flex flex-col gap-2 list-none m-0 p-0">
<li style="--tint:264">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">LM</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Lumen Design</span>
<span class="text-[12.5px] text-tlf02-mut">lumen.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Owner</span>
</a>
</li>
<li style="--tint:160">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">AC</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Acme Corp</span>
<span class="text-[12.5px] text-tlf02-mut">acme.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Admin</span>
</a>
</li>
<li style="--tint:30">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">NW</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Northwind</span>
<span class="text-[12.5px] text-tlf02-mut">northwind.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Member</span>
</a>
</li>
</ul>
<a href="#" class="flex items-center justify-center gap-2 min-h-11 mt-4 p-3 rounded-xl border-[1.5px] border-dashed border-tlf02-dash text-sm font-semibold text-tlf02-mut no-underline transition-colors duration-150 hover:border-tlf02-acc hover:text-tlf02-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf02-ring"><span aria-hidden="true" class="text-[17px] leading-none">+</span> Add another account</a>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}
```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-Tenant Workspace Account Switcher
Source: https://codefronts.com/components/tailwind-css-login-forms/tailwind-multi-tenant-workspace-account-switcher/
The B2B enterprise front door (think Slack or AWS): a returning user picks which workspace to enter from a stack of profile rows, each with a tinted avatar and a role badge. The port keeps the original's best idea — one --tint hue per row drives avatar, badge, hover and ring — because a per-row hue is data, not a design-scale value, and belongs in a style attribute rather than four utilities.
## HTML
```html
<section class="tlf-02 w-full min-h-svh grid place-items-center bg-tlf02-stage font-tlf02-sans px-5 py-8">
<div class="w-[min(420px,100%)] p-6 bg-white border border-tlf02-edge rounded-[20px] shadow-[0_24px_56px_-38px_oklch(0.25_0.06_265/0.55)]">
<header class="mb-4">
<h1 class="text-xl font-bold tracking-tight text-tlf02-ink">Choose a workspace</h1>
<p class="mt-1 text-[13px] text-tlf02-mut">You’re signed in as ana@lumen.io</p>
</header>
<ul class="flex flex-col gap-2 list-none m-0 p-0">
<li style="--tint:264">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">LM</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Lumen Design</span>
<span class="text-[12.5px] text-tlf02-mut">lumen.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Owner</span>
</a>
</li>
<li style="--tint:160">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">AC</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Acme Corp</span>
<span class="text-[12.5px] text-tlf02-mut">acme.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Admin</span>
</a>
</li>
<li style="--tint:30">
<a href="#" class="flex items-center gap-3 min-h-11 p-3 rounded-xl no-underline border border-transparent transition-[background-color,border-color,translate] duration-150 hover:translate-x-0.5 hover:bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_8%,white)] hover:border-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_30%,white)] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[oklch(0.58_0.19_var(--tint)/0.55)] motion-reduce:transition-colors motion-reduce:hover:translate-x-0">
<span aria-hidden="true" class="grid place-items-center flex-none size-11 rounded-full text-sm font-bold text-white bg-[oklch(0.58_0.16_var(--tint))]">NW</span>
<span class="flex-1 min-w-0 flex flex-col gap-0.5">
<span class="text-[15px] font-semibold text-tlf02-ink truncate">Northwind</span>
<span class="text-[12.5px] text-tlf02-mut">northwind.slack.com</span>
</span>
<span class="flex-none text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-full text-[oklch(0.42_0.16_var(--tint))] bg-[color-mix(in_oklch,oklch(0.58_0.16_var(--tint))_16%,white)]">Member</span>
</a>
</li>
</ul>
<a href="#" class="flex items-center justify-center gap-2 min-h-11 mt-4 p-3 rounded-xl border-[1.5px] border-dashed border-tlf02-dash text-sm font-semibold text-tlf02-mut no-underline transition-colors duration-150 hover:border-tlf02-acc hover:text-tlf02-acc focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tlf02-ring"><span aria-hidden="true" class="text-[17px] leading-none">+</span> Add another account</a>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tlf02-stage: #f4f5f9;
--color-tlf02-stage: oklch(0.96 0.005 270);
--color-tlf02-edge: #e7e9f0;
--color-tlf02-edge: oklch(0.92 0.006 270);
--color-tlf02-dash: #cfd4e2;
--color-tlf02-dash: oklch(0.85 0.012 270);
--color-tlf02-ink: #161c2e;
--color-tlf02-ink: oklch(0.23 0.04 270);
--color-tlf02-mut: #656c86;
--color-tlf02-mut: oklch(0.51 0.03 270);
--color-tlf02-acc: #5a63d8;
--color-tlf02-acc: oklch(0.58 0.19 264);
--color-tlf02-ring: #c9cdf4;
--color-tlf02-ring: oklch(0.58 0.19 264 / 0.5);
--color-tlf02-wash: #f0f1f6;
--color-tlf02-wash: oklch(0.94 0.005 270);
--font-tlf02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tlf-02 {
width: 100%;
min-height: 100svh;
}
/* color-mix() fallback: browsers without it get a neutral wash rather than no hover at all. */
@supports not (background: color-mix(in oklch, red 8%, white)) {
.tlf-02 a:hover {
background: var(--color-tlf02-wash);
border-color: var(--color-tlf02-dash);
}
}
```How this works
Per-row data stays a custom property. Tailwind's answer to "three rows, three hues" is not three sets of colour utilities — it is one class list where every colour reads var(--tint), and the hue lands on the row as inline data. Add a fourth workspace and you copy a <li> and change one number.
color-mix() does the tinting Tailwind can't token-ise. An 8% wash of the row hue over white, and a 30% edge of the same, are both arbitrary values here rather than @theme entries, because they depend on a property the theme cannot see.
The whole row is the target. The <a> is the flex container, not a child of it, so the avatar, the name stack and the badge are all inside the click area — and min-w-0 plus truncate on the name column keeps a long workspace name from pushing the badge off the card.
Make it yours
- Tint a workspace by changing its
--tinthue angle — 264 indigo, 160 green, 30 amber. - Swap initials for real avatars: the flex row already reserves a
size-11slot. - Row density lives in two utilities,
gap-2on the list andp-3on the row. - Make rows buttons instead of links if the switch posts rather than navigates — the class list is unchanged.
Gotchas — read before shipping
- Keep the rows in a real
<ul>: assistive tech announces "list, 3 items", which is the whole orientation cue on this screen. - Never let the role badge be colour-only — the text label is what a colour-blind user reads.
oklch(0.58 0.16 var(--tint))inside an arbitrary utility needs the underscore escape for its spaces (oklch(0.58_0.16_var(--tint))); writing real spaces there silently drops the whole class.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
color-mix() in oklch is the gate (Chrome 111, Safari 16.4, Firefox 113). Without it the hover wash falls back to the untinted grey in --color-tlf02-wash.