15 / 33
All 33 in 33 Tailwind CSS Card Hover Effects
- 01Tailwind 3D Tilt & Parallax Card with Depth Layers
- 02Tailwind Glowing Gradient & Glassmorphic Border Card
- 03Tailwind Image Zoom Card with Slide-Up Content Reveal
- 04Tailwind 3D Flip Card (Front to Back) with backface-visibility
- 05Tailwind Sibling De-emphasis Pricing Grid with has-hover
- 06Tailwind Minimalist Elevation Card with Layered Dynamic Shadows
- 07Tailwind Elastic Lift Card with Spring Cubic-Bezier
- 08Tailwind Animated Conic Gradient Border Card with @property
- 09Tailwind Stacked Layer Reveal Card (Fanned Risograph Deck)
- 10Tailwind Holographic Foil Shimmer Card with mix-blend-mode
- 11Tailwind Curtain Reveal Card (Parting Velvet Halves)
- 12Tailwind Corner Peel Card with clip-path Triangles
- 13Tailwind Neon Sign Card with Layered Text-Shadow Glow
- 14Tailwind CRT Scanline Terminal Card with Sweeping Beam
- 15Tailwind Depth Parallax Landscape Card with Layer Multipliers
- 16Tailwind Cursor Spotlight Card with Masked Border Highlight
- 17Tailwind 3D Tilt Product Card with Specular Glare
- 18Tailwind Slide-Up Panel Card with Peek Header Swap
- 19Tailwind Credit Card Flip with Magnetic Strip & CVV
- 20Tailwind RGB Channel-Split Glitch Card
- 21Tailwind Rotating Conic Aura Card with Blurred Gradient Bloom
- 22Tailwind Blob-to-Rectangle Border Morph Card
- 23Tailwind Magnetic Float Orb Card with Pointer Attraction
- 24Tailwind Typewriter Reveal Card with Steps Animation & Caret
- 25Tailwind Magazine Overlay Slide Card (Full-Face Panel Takeover)
- 26Tailwind Breathing Film Grain Card with SVG Turbulence Noise
- 27Tailwind Frosted Glass Intensify Card with Deepening Backdrop Blur
- 28Tailwind Blueprint Reveal Card with Circular clip-path Wipe
- 29Tailwind Cursor-Anchored Shockwave Ripple Card
- 30Tailwind Color-Burn Ignition Card with Grayscale-to-Fire Hover
- 31Tailwind Prismatic Light Sweep Card with Screen Blend Bar
- 32Tailwind Book Cover Open Card with Hinged 3D Rotation
- 33Tailwind Drifting Aurora Ribbons Card with Blurred Gradient Bands
Tailwind Depth Parallax Landscape Card with Layer Multipliers
A dawn-trek card whose ridgelines, sun and foreground hills drift at different rates as the cursor moves. The scene is four gradient layers — no images — and the depth contract lives in data-depth attributes the JS reads, so adding a layer means adding one number.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tch-15 w-full min-h-svh grid place-items-center bg-tch15-bg font-tch15-sans p-8 [perspective:1000px]">
<article data-tch15="root" tabindex="0" class="relative w-[340px] h-[440px] rounded-[22px] overflow-hidden cursor-pointer outline-none transform-3d bg-linear-to-b from-tch15-sky to-tch15-bg shadow-[0_20px_50px_rgb(0_0_0/0.45)] transition-transform duration-200 ease-out focus-visible:outline-3 focus-visible:outline-tch15-sun focus-visible:outline-offset-4 *:absolute *:inset-0 *:transition-transform *:duration-200 *:ease-out *:will-change-transform motion-reduce:transition-none motion-reduce:*:transition-none">
<div data-depth="8" class="opacity-90 bg-[radial-gradient(circle_at_20%_70%,#1b4a63_0_28%,transparent_29%),radial-gradient(circle_at_70%_75%,#1b4a63_0_32%,transparent_33%)]"></div>
<div data-depth="16" class="grid place-items-center">
<div class="size-[130px] -mt-15 rounded-full bg-[radial-gradient(circle,#ffd56b,#ff8c42)] shadow-[0_0_60px_rgb(255_170_80/0.6)]"></div>
</div>
<div data-depth="28" class="bg-[radial-gradient(ellipse_120%_60%_at_30%_110%,#06222e_60%,transparent_61%),radial-gradient(ellipse_120%_50%_at_80%_115%,#04161e_60%,transparent_61%)]"></div>
<div data-depth="40" class="z-5 p-[2.2rem] flex flex-col justify-end text-tch15-ink">
<span class="text-[0.72rem] tracking-[0.28em] uppercase text-tch15-sun">Field Notes</span>
<h2 class="font-tch15-display font-extrabold text-[2.6rem] leading-[0.95] mt-1.5 mb-2">Valley of<br>Long Light</h2>
<p class="text-[0.85rem] leading-[1.6] text-tch15-body">A guided dawn trek through layered ridgelines. Move your cursor to feel the depth.</p>
</div>
</article>
</section><section class="tch-15 w-full min-h-svh grid place-items-center bg-tch15-bg font-tch15-sans p-8 [perspective:1000px]">
<article data-tch15="root" tabindex="0" class="relative w-[340px] h-[440px] rounded-[22px] overflow-hidden cursor-pointer outline-none transform-3d bg-linear-to-b from-tch15-sky to-tch15-bg shadow-[0_20px_50px_rgb(0_0_0/0.45)] transition-transform duration-200 ease-out focus-visible:outline-3 focus-visible:outline-tch15-sun focus-visible:outline-offset-4 *:absolute *:inset-0 *:transition-transform *:duration-200 *:ease-out *:will-change-transform motion-reduce:transition-none motion-reduce:*:transition-none">
<div data-depth="8" class="opacity-90 bg-[radial-gradient(circle_at_20%_70%,#1b4a63_0_28%,transparent_29%),radial-gradient(circle_at_70%_75%,#1b4a63_0_32%,transparent_33%)]"></div>
<div data-depth="16" class="grid place-items-center">
<div class="size-[130px] -mt-15 rounded-full bg-[radial-gradient(circle,#ffd56b,#ff8c42)] shadow-[0_0_60px_rgb(255_170_80/0.6)]"></div>
</div>
<div data-depth="28" class="bg-[radial-gradient(ellipse_120%_60%_at_30%_110%,#06222e_60%,transparent_61%),radial-gradient(ellipse_120%_50%_at_80%_115%,#04161e_60%,transparent_61%)]"></div>
<div data-depth="40" class="z-5 p-[2.2rem] flex flex-col justify-end text-tch15-ink">
<span class="text-[0.72rem] tracking-[0.28em] uppercase text-tch15-sun">Field Notes</span>
<h2 class="font-tch15-display font-extrabold text-[2.6rem] leading-[0.95] mt-1.5 mb-2">Valley of<br>Long Light</h2>
<p class="text-[0.85rem] leading-[1.6] text-tch15-body">A guided dawn trek through layered ridgelines. Move your cursor to feel the depth.</p>
</div>
</article>
</section>@import "tailwindcss";
@theme {
--color-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-15 {
width: 100%;
min-height: 100svh;
}(() => {
const root = document.querySelector('.tch-15');
if (!root) return;
const card = root.querySelector('[data-tch15="root"]');
if (!card) return;
if (!matchMedia('(hover: hover) and (pointer: fine)').matches) return;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const layers = card.querySelectorAll('[data-depth]');
let frame = 0;
card.addEventListener('mousemove', (e) => {
if (frame) return;
frame = requestAnimationFrame(() => {
frame = 0;
const r = card.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
card.style.transform = 'rotateY(' + (x * 10) + 'deg) rotateX(' + (-y * 10) + 'deg)';
layers.forEach((l) => {
const d = parseFloat(l.dataset.depth);
l.style.transform = 'translate(' + (-x * d) + 'px, ' + (-y * d) + 'px)';
});
});
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'rotateY(0deg) rotateX(0deg)';
layers.forEach((l) => { l.style.transform = 'translate(0,0)'; });
});
})();(() => {
const root = document.querySelector('.tch-15');
if (!root) return;
const card = root.querySelector('[data-tch15="root"]');
if (!card) return;
if (!matchMedia('(hover: hover) and (pointer: fine)').matches) return;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const layers = card.querySelectorAll('[data-depth]');
let frame = 0;
card.addEventListener('mousemove', (e) => {
if (frame) return;
frame = requestAnimationFrame(() => {
frame = 0;
const r = card.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
card.style.transform = 'rotateY(' + (x * 10) + 'deg) rotateX(' + (-y * 10) + 'deg)';
layers.forEach((l) => {
const d = parseFloat(l.dataset.depth);
l.style.transform = 'translate(' + (-x * d) + 'px, ' + (-y * d) + 'px)';
});
});
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'rotateY(0deg) rotateX(0deg)';
layers.forEach((l) => { l.style.transform = 'translate(0,0)'; });
});
})();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-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-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=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,800&family=Inter+Tight:wght@400;600&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=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,800&family=Inter+Tight:wght@400;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
*:absolute *:inset-0 *:transition-transformreplaces the CSS version's.layerclass entirely — the layers now need no class of their own beyond their gradient and theirdata-depth.- Keeping depth in a data attribute rather than in per-layer classes means the JS and the markup agree by construction; there is no second place where 28 could become 26.
- UI change from the CSS original: rAF throttling and hard guards on pointer type and reduced motion (the original attached unconditionally), plus a focusable card with a visible ring.
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 Depth Parallax Landscape Card with Layer Multipliers
Source: https://codefronts.com/motion/tailwind-css-card-hover-effects/tailwind-depth-parallax-landscape-card/
A dawn-trek card whose ridgelines, sun and foreground hills drift at different rates as the cursor moves. The scene is four gradient layers — no images — and the depth contract lives in data-depth attributes the JS reads, so adding a layer means adding one number.
## HTML
```html
<section class="tch-15 w-full min-h-svh grid place-items-center bg-tch15-bg font-tch15-sans p-8 [perspective:1000px]">
<article data-tch15="root" tabindex="0" class="relative w-[340px] h-[440px] rounded-[22px] overflow-hidden cursor-pointer outline-none transform-3d bg-linear-to-b from-tch15-sky to-tch15-bg shadow-[0_20px_50px_rgb(0_0_0/0.45)] transition-transform duration-200 ease-out focus-visible:outline-3 focus-visible:outline-tch15-sun focus-visible:outline-offset-4 *:absolute *:inset-0 *:transition-transform *:duration-200 *:ease-out *:will-change-transform motion-reduce:transition-none motion-reduce:*:transition-none">
<div data-depth="8" class="opacity-90 bg-[radial-gradient(circle_at_20%_70%,#1b4a63_0_28%,transparent_29%),radial-gradient(circle_at_70%_75%,#1b4a63_0_32%,transparent_33%)]"></div>
<div data-depth="16" class="grid place-items-center">
<div class="size-[130px] -mt-15 rounded-full bg-[radial-gradient(circle,#ffd56b,#ff8c42)] shadow-[0_0_60px_rgb(255_170_80/0.6)]"></div>
</div>
<div data-depth="28" class="bg-[radial-gradient(ellipse_120%_60%_at_30%_110%,#06222e_60%,transparent_61%),radial-gradient(ellipse_120%_50%_at_80%_115%,#04161e_60%,transparent_61%)]"></div>
<div data-depth="40" class="z-5 p-[2.2rem] flex flex-col justify-end text-tch15-ink">
<span class="text-[0.72rem] tracking-[0.28em] uppercase text-tch15-sun">Field Notes</span>
<h2 class="font-tch15-display font-extrabold text-[2.6rem] leading-[0.95] mt-1.5 mb-2">Valley of<br>Long Light</h2>
<p class="text-[0.85rem] leading-[1.6] text-tch15-body">A guided dawn trek through layered ridgelines. Move your cursor to feel the depth.</p>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-15 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.tch-15');
if (!root) return;
const card = root.querySelector('[data-tch15="root"]');
if (!card) return;
if (!matchMedia('(hover: hover) and (pointer: fine)').matches) return;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const layers = card.querySelectorAll('[data-depth]');
let frame = 0;
card.addEventListener('mousemove', (e) => {
if (frame) return;
frame = requestAnimationFrame(() => {
frame = 0;
const r = card.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
card.style.transform = 'rotateY(' + (x * 10) + 'deg) rotateX(' + (-y * 10) + 'deg)';
layers.forEach((l) => {
const d = parseFloat(l.dataset.depth);
l.style.transform = 'translate(' + (-x * d) + 'px, ' + (-y * d) + 'px)';
});
});
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'rotateY(0deg) rotateX(0deg)';
layers.forEach((l) => { l.style.transform = 'translate(0,0)'; });
});
})();
```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 Depth Parallax Landscape Card with Layer Multipliers
Source: https://codefronts.com/motion/tailwind-css-card-hover-effects/tailwind-depth-parallax-landscape-card/
A dawn-trek card whose ridgelines, sun and foreground hills drift at different rates as the cursor moves. The scene is four gradient layers — no images — and the depth contract lives in data-depth attributes the JS reads, so adding a layer means adding one number.
## HTML
```html
<section class="tch-15 w-full min-h-svh grid place-items-center bg-tch15-bg font-tch15-sans p-8 [perspective:1000px]">
<article data-tch15="root" tabindex="0" class="relative w-[340px] h-[440px] rounded-[22px] overflow-hidden cursor-pointer outline-none transform-3d bg-linear-to-b from-tch15-sky to-tch15-bg shadow-[0_20px_50px_rgb(0_0_0/0.45)] transition-transform duration-200 ease-out focus-visible:outline-3 focus-visible:outline-tch15-sun focus-visible:outline-offset-4 *:absolute *:inset-0 *:transition-transform *:duration-200 *:ease-out *:will-change-transform motion-reduce:transition-none motion-reduce:*:transition-none">
<div data-depth="8" class="opacity-90 bg-[radial-gradient(circle_at_20%_70%,#1b4a63_0_28%,transparent_29%),radial-gradient(circle_at_70%_75%,#1b4a63_0_32%,transparent_33%)]"></div>
<div data-depth="16" class="grid place-items-center">
<div class="size-[130px] -mt-15 rounded-full bg-[radial-gradient(circle,#ffd56b,#ff8c42)] shadow-[0_0_60px_rgb(255_170_80/0.6)]"></div>
</div>
<div data-depth="28" class="bg-[radial-gradient(ellipse_120%_60%_at_30%_110%,#06222e_60%,transparent_61%),radial-gradient(ellipse_120%_50%_at_80%_115%,#04161e_60%,transparent_61%)]"></div>
<div data-depth="40" class="z-5 p-[2.2rem] flex flex-col justify-end text-tch15-ink">
<span class="text-[0.72rem] tracking-[0.28em] uppercase text-tch15-sun">Field Notes</span>
<h2 class="font-tch15-display font-extrabold text-[2.6rem] leading-[0.95] mt-1.5 mb-2">Valley of<br>Long Light</h2>
<p class="text-[0.85rem] leading-[1.6] text-tch15-body">A guided dawn trek through layered ridgelines. Move your cursor to feel the depth.</p>
</div>
</article>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tch15-bg: #0b1d2a;
--color-tch15-bg: oklch(0.23 0.04 240);
--color-tch15-sky: #2c6e8f;
--color-tch15-sky: oklch(0.52 0.08 235);
--color-tch15-ink: #eaf4fa;
--color-tch15-ink: oklch(0.96 0.01 230);
--color-tch15-body: #b9d0dd;
--color-tch15-body: oklch(0.83 0.03 230);
--color-tch15-sun: #ffd56b;
--color-tch15-sun: oklch(0.88 0.13 85);
--font-tch15-sans: 'Inter Tight', system-ui, sans-serif;
--font-tch15-display: 'Bricolage Grotesque', system-ui, sans-serif;
}
.tch-15 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.tch-15');
if (!root) return;
const card = root.querySelector('[data-tch15="root"]');
if (!card) return;
if (!matchMedia('(hover: hover) and (pointer: fine)').matches) return;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const layers = card.querySelectorAll('[data-depth]');
let frame = 0;
card.addEventListener('mousemove', (e) => {
if (frame) return;
frame = requestAnimationFrame(() => {
frame = 0;
const r = card.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
card.style.transform = 'rotateY(' + (x * 10) + 'deg) rotateX(' + (-y * 10) + 'deg)';
layers.forEach((l) => {
const d = parseFloat(l.dataset.depth);
l.style.transform = 'translate(' + (-x * d) + 'px, ' + (-y * d) + 'px)';
});
});
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'rotateY(0deg) rotateX(0deg)';
layers.forEach((l) => { l.style.transform = 'translate(0,0)'; });
});
})();
```How this works
Depth is data, not CSS. Each layer declares data-depth="8", 16, 28, 40. The handler normalises the pointer to -0.5…0.5 and translates each layer by -x * depth. Differential rate is the entire illusion, and it is tunable per layer without touching a class.
The card tilts too. A gentle 10° rotateY/rotateX on the card under [perspective:1000px] sits under the layer drift, so the scene leans as well as slides.
The scene is gradients. Far ridges are two radials, the sun is a radial orb with a glow shadow, the near hills are two wide ellipses. Cheap, sharp at any DPI, and rethemeable from tokens.
Make it yours
- Depth spread: keep the ratios and scale them — 8/16/28/40 is pronounced, halve them all for restrained.
- Add depth-of-field with
blur-[1px]on the far layer so it reads as genuinely behind. - Retheme sky, ridge and sun from the four
@themecolours; a dusk palette needs no structural change.
Gotchas — read before shipping
- Throttle with
requestAnimationFrame. Writing four transforms per rawmousemoveis the fastest way to make a nice effect feel cheap. - Guard on
(hover: hover) and (pointer: fine)— synthetic touchmousemovemakes the layers twitch. will-change-transformis applied to the four layers only, not the card; blanketwill-changecosts more than it returns.- Parallax is decoration. Any focusable child still needs its own visible focus ring.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Custom properties, 3D transforms and pointer events are universally supported; the floor is Tailwind v4's generated sheet.