01 / 20
All 20 in 20 Tailwind Liquid Glass Effects
- 01Tailwind Liquid Glass Card with Hover Refraction
- 02Tailwind Liquid Glass Button with Glossy Highlight
- 03Tailwind Liquid Glass Navigation Bar
- 04Tailwind Liquid Gooey Toggle Switch
- 05Tailwind Liquid Glass Modal Dialog
- 06Tailwind Liquid Glass Theme Switcher
- 07Tailwind Liquid Glass Input Form Fields
- 08Tailwind SVG Displacement Liquid Glass Background
- 09Tailwind Liquid Glass Dropdown Menu
- 10Tailwind Liquid Glass Pricing Table
- 11Tailwind Liquid Glass Tooltip Popover
- 12Tailwind Liquid Glass Audio Player UI
- 13Tailwind Liquid Glass Range Slider
- 14Tailwind Liquid Glass Toast Notification
- 15Tailwind Liquid Glass Sidebar Navigation
- 16Tailwind Liquid Glass Accordion
- 17Tailwind Liquid Glass Login Card
- 18Tailwind Liquid Glass Floating Action Button
- 19Tailwind Liquid Glass Loading Skeleton
- 20Tailwind Liquid Glass Multi-Step Form Wizard
Tailwind Liquid Glass Card with Hover Refraction
The premium product card, ported so the whole material is legible in one class list: backdrop-blur-[18px] backdrop-saturate-180 is the frost, bg-white/12 plus border-white/35 plus an inset hairline shadow is the thickness, and a before: radial gloss reads two custom properties the pointer handler writes. The 3D tilt is an arbitrary [transform:rotateX(var(--ry))_rotateY(var(--rx))], so the JS never touches a style beyond four variables.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="lgt-01 grid w-full min-h-svh place-items-center p-15 font-lgt01-sans [perspective:1100px] bg-[image:url('https://picsum.photos/seed/lgcard/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass card with hover refraction">
<article id="lgt-01-card" tabindex="0" class="group relative isolate w-[min(340px,90vw)] cursor-pointer overflow-hidden rounded-[26px] border border-white/35 bg-white/12 p-6.5 pb-5.5 text-white backdrop-blur-[18px] backdrop-saturate-180 shadow-[0_30px_60px_-30px_rgba(0,0,0,.65),inset_0_1px_0_rgba(255,255,255,.5)] transition-[transform,box-shadow] duration-500 ease-lgt01 [transform-style:preserve-3d] [transform:rotateX(var(--ry,0deg))_rotateY(var(--rx,0deg))] hover:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white/90 focus-visible:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] motion-reduce:transition-[box-shadow] motion-reduce:duration-200 before:pointer-events-none before:absolute before:inset-0 before:z-2 before:rounded-[inherit] before:transition-opacity before:duration-300 before:[background:radial-gradient(220px_220px_at_var(--mx,30%)_var(--my,20%),rgba(255,255,255,.55),rgba(255,255,255,0)_60%)] before:[opacity:var(--gloss,.5)] after:pointer-events-none after:absolute after:inset-0 after:z-1 after:rounded-[inherit] after:[background:linear-gradient(120deg,transparent_30%,rgba(255,255,255,.18)_46%,transparent_62%)] motion-reduce:after:hidden">
<span class="relative z-3 inline-block rounded-full border border-white/40 bg-white/22 px-2.75 py-1.25 text-[11px] font-extrabold uppercase tracking-[0.12em]">New</span>
<h3 class="relative z-3 mt-16 mb-2 text-2xl font-extrabold tracking-[-0.02em]">Aurora Headphones</h3>
<p class="relative z-3 text-sm leading-relaxed text-white [text-shadow:0_1px_8px_rgba(0,0,0,.45)]">Spatial audio, 40-hour battery, and a machined-glass shell that catches the light like the UI around it.</p>
<div class="relative z-3 mt-5 flex items-center justify-between">
<span class="text-[22px] font-extrabold">$299</span>
<span class="rounded-full bg-white/90 px-4 py-2 text-sm font-semibold text-lgt01-deep">View →</span>
</div>
</article>
</section><section class="lgt-01 grid w-full min-h-svh place-items-center p-15 font-lgt01-sans [perspective:1100px] bg-[image:url('https://picsum.photos/seed/lgcard/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass card with hover refraction">
<article id="lgt-01-card" tabindex="0" class="group relative isolate w-[min(340px,90vw)] cursor-pointer overflow-hidden rounded-[26px] border border-white/35 bg-white/12 p-6.5 pb-5.5 text-white backdrop-blur-[18px] backdrop-saturate-180 shadow-[0_30px_60px_-30px_rgba(0,0,0,.65),inset_0_1px_0_rgba(255,255,255,.5)] transition-[transform,box-shadow] duration-500 ease-lgt01 [transform-style:preserve-3d] [transform:rotateX(var(--ry,0deg))_rotateY(var(--rx,0deg))] hover:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white/90 focus-visible:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] motion-reduce:transition-[box-shadow] motion-reduce:duration-200 before:pointer-events-none before:absolute before:inset-0 before:z-2 before:rounded-[inherit] before:transition-opacity before:duration-300 before:[background:radial-gradient(220px_220px_at_var(--mx,30%)_var(--my,20%),rgba(255,255,255,.55),rgba(255,255,255,0)_60%)] before:[opacity:var(--gloss,.5)] after:pointer-events-none after:absolute after:inset-0 after:z-1 after:rounded-[inherit] after:[background:linear-gradient(120deg,transparent_30%,rgba(255,255,255,.18)_46%,transparent_62%)] motion-reduce:after:hidden">
<span class="relative z-3 inline-block rounded-full border border-white/40 bg-white/22 px-2.75 py-1.25 text-[11px] font-extrabold uppercase tracking-[0.12em]">New</span>
<h3 class="relative z-3 mt-16 mb-2 text-2xl font-extrabold tracking-[-0.02em]">Aurora Headphones</h3>
<p class="relative z-3 text-sm leading-relaxed text-white [text-shadow:0_1px_8px_rgba(0,0,0,.45)]">Spatial audio, 40-hour battery, and a machined-glass shell that catches the light like the UI around it.</p>
<div class="relative z-3 mt-5 flex items-center justify-between">
<span class="text-[22px] font-extrabold">$299</span>
<span class="rounded-full bg-white/90 px-4 py-2 text-sm font-semibold text-lgt01-deep">View →</span>
</div>
</article>
</section>@import "tailwindcss";
@theme {
--color-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}@import "tailwindcss";
@theme {
--color-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}(() => {
const card = document.querySelector('#lgt-01-card');
if (!card) return;
const MAX_TILT = 8;
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const onMove = e => {
const r = card.getBoundingClientRect();
const px = (e.clientX - r.left) / r.width;
const py = (e.clientY - r.top) / r.height;
card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
card.style.setProperty('--gloss', '.85');
if (reduce) return;
card.style.setProperty('--rx', ((px - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
card.style.setProperty('--ry', (-(py - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
};
const onLeave = () => {
card.style.setProperty('--gloss', '.5');
card.style.setProperty('--rx', '0deg');
card.style.setProperty('--ry', '0deg');
};
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', onLeave);
card.cleanup = () => { card.removeEventListener('pointermove', onMove); card.removeEventListener('pointerleave', onLeave); };
})();(() => {
const card = document.querySelector('#lgt-01-card');
if (!card) return;
const MAX_TILT = 8;
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const onMove = e => {
const r = card.getBoundingClientRect();
const px = (e.clientX - r.left) / r.width;
const py = (e.clientY - r.top) / r.height;
card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
card.style.setProperty('--gloss', '.85');
if (reduce) return;
card.style.setProperty('--rx', ((px - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
card.style.setProperty('--ry', (-(py - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
};
const onLeave = () => {
card.style.setProperty('--gloss', '.5');
card.style.setProperty('--rx', '0deg');
card.style.setProperty('--ry', '0deg');
};
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', onLeave);
card.cleanup = () => { card.removeEventListener('pointermove', onMove); card.removeEventListener('pointerleave', onLeave); };
})();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-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}@import "tailwindcss";
@theme {
--color-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}<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=Manrope:wght@400;600;800&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=Manrope:wght@400;600;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The four-utility glass recipe —
bg-white/12,border-white/35,backdrop-blur-[18px] backdrop-saturate-180, inset hairline shadow — replaces a 6-declaration CSS rule and is reused verbatim in all twenty demos. - Both pseudo-element layers move into
before:/after:variants, so the gloss and the specular sheen are visible on the element that owns them. - The script sets custom properties only. It never writes a style that carries design intent, which keeps the whole appearance in the class list where the compiler and the reader can both see it.
[perspective:1100px]and[transform-style:preserve-3d]stay arbitrary — they are scene plumbing, not design-scale values.- Safari's
-webkit-backdrop-filterprefix is emitted by Tailwind automatically; the hand-written CSS version had to declare it twice everywhere.
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 Liquid Glass Card with Hover Refraction
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-card-with-hover-refraction/
The premium product card, ported so the whole material is legible in one class list: backdrop-blur-[18px] backdrop-saturate-180 is the frost, bg-white/12 plus border-white/35 plus an inset hairline shadow is the thickness, and a before: radial gloss reads two custom properties the pointer handler writes. The 3D tilt is an arbitrary [transform:rotateX(var(--ry))_rotateY(var(--rx))], so the JS never touches a style beyond four variables.
## HTML
```html
<section class="lgt-01 grid w-full min-h-svh place-items-center p-15 font-lgt01-sans [perspective:1100px] bg-[image:url('https://picsum.photos/seed/lgcard/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass card with hover refraction">
<article id="lgt-01-card" tabindex="0" class="group relative isolate w-[min(340px,90vw)] cursor-pointer overflow-hidden rounded-[26px] border border-white/35 bg-white/12 p-6.5 pb-5.5 text-white backdrop-blur-[18px] backdrop-saturate-180 shadow-[0_30px_60px_-30px_rgba(0,0,0,.65),inset_0_1px_0_rgba(255,255,255,.5)] transition-[transform,box-shadow] duration-500 ease-lgt01 [transform-style:preserve-3d] [transform:rotateX(var(--ry,0deg))_rotateY(var(--rx,0deg))] hover:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white/90 focus-visible:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] motion-reduce:transition-[box-shadow] motion-reduce:duration-200 before:pointer-events-none before:absolute before:inset-0 before:z-2 before:rounded-[inherit] before:transition-opacity before:duration-300 before:[background:radial-gradient(220px_220px_at_var(--mx,30%)_var(--my,20%),rgba(255,255,255,.55),rgba(255,255,255,0)_60%)] before:[opacity:var(--gloss,.5)] after:pointer-events-none after:absolute after:inset-0 after:z-1 after:rounded-[inherit] after:[background:linear-gradient(120deg,transparent_30%,rgba(255,255,255,.18)_46%,transparent_62%)] motion-reduce:after:hidden">
<span class="relative z-3 inline-block rounded-full border border-white/40 bg-white/22 px-2.75 py-1.25 text-[11px] font-extrabold uppercase tracking-[0.12em]">New</span>
<h3 class="relative z-3 mt-16 mb-2 text-2xl font-extrabold tracking-[-0.02em]">Aurora Headphones</h3>
<p class="relative z-3 text-sm leading-relaxed text-white [text-shadow:0_1px_8px_rgba(0,0,0,.45)]">Spatial audio, 40-hour battery, and a machined-glass shell that catches the light like the UI around it.</p>
<div class="relative z-3 mt-5 flex items-center justify-between">
<span class="text-[22px] font-extrabold">$299</span>
<span class="rounded-full bg-white/90 px-4 py-2 text-sm font-semibold text-lgt01-deep">View →</span>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}
```
## JavaScript
```js
(() => {
const card = document.querySelector('#lgt-01-card');
if (!card) return;
const MAX_TILT = 8;
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const onMove = e => {
const r = card.getBoundingClientRect();
const px = (e.clientX - r.left) / r.width;
const py = (e.clientY - r.top) / r.height;
card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
card.style.setProperty('--gloss', '.85');
if (reduce) return;
card.style.setProperty('--rx', ((px - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
card.style.setProperty('--ry', (-(py - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
};
const onLeave = () => {
card.style.setProperty('--gloss', '.5');
card.style.setProperty('--rx', '0deg');
card.style.setProperty('--ry', '0deg');
};
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', onLeave);
card.cleanup = () => { card.removeEventListener('pointermove', onMove); card.removeEventListener('pointerleave', onLeave); };
})();
```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 Liquid Glass Card with Hover Refraction
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-card-with-hover-refraction/
The premium product card, ported so the whole material is legible in one class list: backdrop-blur-[18px] backdrop-saturate-180 is the frost, bg-white/12 plus border-white/35 plus an inset hairline shadow is the thickness, and a before: radial gloss reads two custom properties the pointer handler writes. The 3D tilt is an arbitrary [transform:rotateX(var(--ry))_rotateY(var(--rx))], so the JS never touches a style beyond four variables.
## HTML
```html
<section class="lgt-01 grid w-full min-h-svh place-items-center p-15 font-lgt01-sans [perspective:1100px] bg-[image:url('https://picsum.photos/seed/lgcard/1200/900')] bg-cover bg-center bg-fixed" aria-label="Tailwind liquid glass card with hover refraction">
<article id="lgt-01-card" tabindex="0" class="group relative isolate w-[min(340px,90vw)] cursor-pointer overflow-hidden rounded-[26px] border border-white/35 bg-white/12 p-6.5 pb-5.5 text-white backdrop-blur-[18px] backdrop-saturate-180 shadow-[0_30px_60px_-30px_rgba(0,0,0,.65),inset_0_1px_0_rgba(255,255,255,.5)] transition-[transform,box-shadow] duration-500 ease-lgt01 [transform-style:preserve-3d] [transform:rotateX(var(--ry,0deg))_rotateY(var(--rx,0deg))] hover:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white/90 focus-visible:shadow-[0_40px_80px_-28px_rgba(0,0,0,.7),inset_0_1px_0_rgba(255,255,255,.6)] motion-reduce:transition-[box-shadow] motion-reduce:duration-200 before:pointer-events-none before:absolute before:inset-0 before:z-2 before:rounded-[inherit] before:transition-opacity before:duration-300 before:[background:radial-gradient(220px_220px_at_var(--mx,30%)_var(--my,20%),rgba(255,255,255,.55),rgba(255,255,255,0)_60%)] before:[opacity:var(--gloss,.5)] after:pointer-events-none after:absolute after:inset-0 after:z-1 after:rounded-[inherit] after:[background:linear-gradient(120deg,transparent_30%,rgba(255,255,255,.18)_46%,transparent_62%)] motion-reduce:after:hidden">
<span class="relative z-3 inline-block rounded-full border border-white/40 bg-white/22 px-2.75 py-1.25 text-[11px] font-extrabold uppercase tracking-[0.12em]">New</span>
<h3 class="relative z-3 mt-16 mb-2 text-2xl font-extrabold tracking-[-0.02em]">Aurora Headphones</h3>
<p class="relative z-3 text-sm leading-relaxed text-white [text-shadow:0_1px_8px_rgba(0,0,0,.45)]">Spatial audio, 40-hour battery, and a machined-glass shell that catches the light like the UI around it.</p>
<div class="relative z-3 mt-5 flex items-center justify-between">
<span class="text-[22px] font-extrabold">$299</span>
<span class="rounded-full bg-white/90 px-4 py-2 text-sm font-semibold text-lgt01-deep">View →</span>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-lgt01-deep: #1a1330;
--color-lgt01-deep: oklch(0.26 0.07 300);
--font-lgt01-sans: 'Manrope', system-ui, sans-serif;
--ease-lgt01: cubic-bezier(0.2, 0.9, 0.2, 1);
}
```
## JavaScript
```js
(() => {
const card = document.querySelector('#lgt-01-card');
if (!card) return;
const MAX_TILT = 8;
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const onMove = e => {
const r = card.getBoundingClientRect();
const px = (e.clientX - r.left) / r.width;
const py = (e.clientY - r.top) / r.height;
card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
card.style.setProperty('--gloss', '.85');
if (reduce) return;
card.style.setProperty('--rx', ((px - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
card.style.setProperty('--ry', (-(py - .5) * MAX_TILT * 2).toFixed(2) + 'deg');
};
const onLeave = () => {
card.style.setProperty('--gloss', '.5');
card.style.setProperty('--rx', '0deg');
card.style.setProperty('--ry', '0deg');
};
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', onLeave);
card.cleanup = () => { card.removeEventListener('pointermove', onMove); card.removeEventListener('pointerleave', onLeave); };
})();
```How this works
The material is four utilities. A translucent fill, a hairline border, an inset rim-light and a paired blur/saturate. Written together they are the entire Liquid Glass surface, and every other demo in this collection reuses the same four:
class="bg-white/12 border border-white/35
backdrop-blur-[18px] backdrop-saturate-180
shadow-[0_30px_60px_-30px_rgba(0,0,0,.65),inset_0_1px_0_rgba(255,255,255,.5)]"The gloss is a pseudo-element variant, not a child div. before: paints a radial highlight positioned from --mx / --my, and its opacity reads --gloss. Because the position lives in variables, the pointer handler writes numbers rather than CSS, and the compiler still sees a static class string.
Tilt is an arbitrary transform. [transform:rotateX(var(--ry,0deg))_rotateY(var(--rx,0deg))] inside a [perspective:1100px] parent. The underscores are the space escape; they compile to real spaces. On pointerleave the variables reset and the transition springs the card back.
Isolation is not optional. isolate gives the card its own stacking context so its backdrop-filter samples the page background and not its siblings — the single most common cause of muddy glass when several frosted cards sit in a grid.
Make it yours
- Push the frost with
backdrop-blur-[24px], but treat 24px as the ceiling — beyond it the paint cost starts showing up in INP on mid-range Android. - Retint the glass by editing the opacity modifier:
bg-white/12→bg-white/20for a milkier surface, orbg-black/20for smoked glass on light backgrounds. - Tune tilt with the
MAX_TILTconstant in the script; 8 degrees is the point where it still reads as glass rather than as a toy. - Change the gloss size and colour in the
before:[background:radial-gradient(...)]arbitrary value. - Add a second accent by giving the card a
--color-lgt01-accenttheme entry and using it on the CTA capsule.
Gotchas — read before shipping
- backdrop-filter needs something non-opaque behind it. Over a flat
bg-slate-900the blur has nothing to sample and the card looks like a plain tinted box — put it over an image or a gradient. - Tailwind emits
-webkit-backdrop-filteralongside the standard property, so the Safari prefix you hand-wrote in CSS is no longer your problem. - Without
isolatea frosted card samples its frosted siblings, and a grid of them turns to mush after the second row. - Arbitrary values cannot contain spaces — use the underscore escape. A literal space silently ends the class and the whole declaration disappears.
- Class strings must be statically visible to the compiler; building
[transform:rotate(${deg}deg)]at runtime produces nothing. Write to a custom property instead, as the script here does. - Body text over glass fails WCAG fast. Keep it at full opacity with a text-shadow, never
text-white/60over a photograph.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4's generated sheet sets the floor (cascade layers, @property, color-mix). backdrop-filter itself goes back to Chrome 76 / Safari 14 / Firefox 103, and the pointer tilt is progressive enhancement — with no JS the card is a static, fully readable glass panel.