06 / 20
All 20 in 20 Tailwind CSS Image Hover Effects
- 01Tailwind Image Hover Zoom In Card
- 02Tailwind Hover Reveal Text Over Image
- 03Tailwind E-Commerce Product Image Swap on Hover
- 04Tailwind Grid Image Blur Overlay Text Hover
- 05Tailwind Card Slide Up Caption on Hover
- 06Tailwind Grayscale to Color Image Hover
- 07Tailwind Image Color Overlay Tint on Hover
- 08Tailwind Neumorphic Image Card Hover Effect
- 09Tailwind Glassmorphism Overlay on Image Hover
- 10Tailwind Glowing Neon Border Image Hover
- 11Tailwind Image Clip-Path Transition Hover
- 12Tailwind Image Tilt 3D Perspective Hover
- 13Tailwind Shining Light Glare Image Hover
- 14Tailwind Image Split Transition Hover
- 15Tailwind Image Ripple Animation Hover
- 16Tailwind Circular Team Profile Avatar Hover
- 17Tailwind Fullscreen Background Image Hover
- 18Tailwind Masonry Gallery Zoom Hover
- 19Tailwind Image Icon Reveal Center Hover
- 20Tailwind Image Card Flip 3D Animation Hover
Tailwind Grayscale to Color Image Hover
Three filter functions transitioned as one unit — and in utilities they are three named classes: grayscale brightness-75 saturate-0 at rest, group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 on hover. The 20% saturation overshoot is what makes the colour land rather than merely appear.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tih-06 w-full min-h-svh grid place-items-center bg-tih06-bg font-tih06-sans px-6 py-10" aria-label="Grayscale to colour image hover demo">
<div class="grid w-full max-w-195 gap-3.5 grid-cols-2 sm:grid-cols-4">
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 group-hover:ring-[oklch(0.79_0.13_205/0.4)] hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=47'),linear-gradient(135deg,oklch(0.3_0.05_250),oklch(0.4_0.17_265),oklch(0.62_0.19_260),oklch(0.82_0.09_250))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Aria Chen</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Lead Engineer</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=12'),linear-gradient(135deg,oklch(0.25_0.08_45),oklch(0.45_0.15_45),oklch(0.63_0.22_28),oklch(0.8_0.1_25))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Marcus Webb</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Creative Director</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=45'),linear-gradient(135deg,oklch(0.3_0.08_165),oklch(0.4_0.1_165),oklch(0.7_0.15_165),oklch(0.86_0.12_165))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Priya Nair</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Research Scientist</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=33'),linear-gradient(135deg,oklch(0.25_0.13_305),oklch(0.45_0.22_300),oklch(0.63_0.21_300),oklch(0.89_0.06_305))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Leo Santos</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Product Strategist</p>
</div>
</article>
</div>
</section><section class="tih-06 w-full min-h-svh grid place-items-center bg-tih06-bg font-tih06-sans px-6 py-10" aria-label="Grayscale to colour image hover demo">
<div class="grid w-full max-w-195 gap-3.5 grid-cols-2 sm:grid-cols-4">
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 group-hover:ring-[oklch(0.79_0.13_205/0.4)] hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=47'),linear-gradient(135deg,oklch(0.3_0.05_250),oklch(0.4_0.17_265),oklch(0.62_0.19_260),oklch(0.82_0.09_250))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Aria Chen</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Lead Engineer</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=12'),linear-gradient(135deg,oklch(0.25_0.08_45),oklch(0.45_0.15_45),oklch(0.63_0.22_28),oklch(0.8_0.1_25))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Marcus Webb</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Creative Director</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=45'),linear-gradient(135deg,oklch(0.3_0.08_165),oklch(0.4_0.1_165),oklch(0.7_0.15_165),oklch(0.86_0.12_165))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Priya Nair</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Research Scientist</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=33'),linear-gradient(135deg,oklch(0.25_0.13_305),oklch(0.45_0.22_300),oklch(0.63_0.21_300),oklch(0.89_0.06_305))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Leo Santos</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Product Strategist</p>
</div>
</article>
</div>
</section>@import "tailwindcss";
@theme {
--color-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}@import "tailwindcss";
@theme {
--color-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}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-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}@import "tailwindcss";
@theme {
--color-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}<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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- A three-function
filterdeclaration becomes three composable utilities, and the hovered stack is three more. This is the property where Tailwind's model is strictly better than hand-written CSS: partial states are token changes. - The
::afterborder pseudo-element disappears entirely, replaced byring-2 ring-transparentplus ahover:ring colour — one fewer box, and nopointer-events:noneto remember. transition-[filter,scale]names both animating properties in one declaration, which is the documented workaround for the Firefox filter-plus-transform stutter the CSS version calls out.- The emoji's own grayscale is a second, independent filter on a child — trivial as a utility, a whole extra rule in CSS.
motion-reduce:lands the portraits in full colour immediately. The alternative (a slow fade) is exactly what the media query exists to prevent.- Nothing but the root rule and
::selectionstays in the stylesheet; the border pseudo-element was the only CSS-only piece and utilities absorbed it.
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 Grayscale to Color Image Hover
Source: https://codefronts.com/motion/tailwind-css-image-hover-effects/tailwind-grayscale-to-color-image-hover/
Three filter functions transitioned as one unit — and in utilities they are three named classes: grayscale brightness-75 saturate-0 at rest, group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 on hover. The 20% saturation overshoot is what makes the colour land rather than merely appear.
## HTML
```html
<section class="tih-06 w-full min-h-svh grid place-items-center bg-tih06-bg font-tih06-sans px-6 py-10" aria-label="Grayscale to colour image hover demo">
<div class="grid w-full max-w-195 gap-3.5 grid-cols-2 sm:grid-cols-4">
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 group-hover:ring-[oklch(0.79_0.13_205/0.4)] hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=47'),linear-gradient(135deg,oklch(0.3_0.05_250),oklch(0.4_0.17_265),oklch(0.62_0.19_260),oklch(0.82_0.09_250))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Aria Chen</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Lead Engineer</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=12'),linear-gradient(135deg,oklch(0.25_0.08_45),oklch(0.45_0.15_45),oklch(0.63_0.22_28),oklch(0.8_0.1_25))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Marcus Webb</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Creative Director</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=45'),linear-gradient(135deg,oklch(0.3_0.08_165),oklch(0.4_0.1_165),oklch(0.7_0.15_165),oklch(0.86_0.12_165))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Priya Nair</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Research Scientist</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=33'),linear-gradient(135deg,oklch(0.25_0.13_305),oklch(0.45_0.22_300),oklch(0.63_0.21_300),oklch(0.89_0.06_305))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Leo Santos</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Product Strategist</p>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}
```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 Grayscale to Color Image Hover
Source: https://codefronts.com/motion/tailwind-css-image-hover-effects/tailwind-grayscale-to-color-image-hover/
Three filter functions transitioned as one unit — and in utilities they are three named classes: grayscale brightness-75 saturate-0 at rest, group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 on hover. The 20% saturation overshoot is what makes the colour land rather than merely appear.
## HTML
```html
<section class="tih-06 w-full min-h-svh grid place-items-center bg-tih06-bg font-tih06-sans px-6 py-10" aria-label="Grayscale to colour image hover demo">
<div class="grid w-full max-w-195 gap-3.5 grid-cols-2 sm:grid-cols-4">
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 group-hover:ring-[oklch(0.79_0.13_205/0.4)] hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=47'),linear-gradient(135deg,oklch(0.3_0.05_250),oklch(0.4_0.17_265),oklch(0.62_0.19_260),oklch(0.82_0.09_250))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Aria Chen</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Lead Engineer</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=12'),linear-gradient(135deg,oklch(0.25_0.08_45),oklch(0.45_0.15_45),oklch(0.63_0.22_28),oklch(0.8_0.1_25))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Marcus Webb</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Creative Director</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=45'),linear-gradient(135deg,oklch(0.3_0.08_165),oklch(0.4_0.1_165),oklch(0.7_0.15_165),oklch(0.86_0.12_165))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Priya Nair</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Research Scientist</p>
</div>
</article>
<article class="group relative overflow-hidden rounded-[10px] aspect-[3/4] ring-2 ring-transparent transition-[box-shadow] duration-550 ease-tih06 hover:ring-[oklch(0.79_0.13_205/0.4)] motion-reduce:transition-none">
<div class="absolute inset-0 grid place-items-center bg-[image:url('https://i.pravatar.cc/400?img=33'),linear-gradient(135deg,oklch(0.25_0.13_305),oklch(0.45_0.22_300),oklch(0.63_0.21_300),oklch(0.89_0.06_305))] bg-cover bg-center bg-no-repeat grayscale brightness-75 saturate-0 transition-[filter,scale] duration-550 ease-tih06 group-hover:grayscale-0 group-hover:brightness-100 group-hover:saturate-120 group-hover:scale-104 motion-reduce:transition-none motion-reduce:grayscale-0 motion-reduce:brightness-100 motion-reduce:saturate-100 motion-reduce:group-hover:scale-100">
</div>
<div class="absolute inset-x-0 bottom-0 p-3 bg-linear-[to_top,oklch(0_0_0/0.8),transparent]">
<p class="text-xs font-bold text-tih06-text">Leo Santos</p>
<p class="mt-0.5 text-[10px] text-tih06-muted transition-colors duration-550 ease-tih06 group-hover:text-tih06-acc motion-reduce:transition-none">Product Strategist</p>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tih06-bg: #09090f;
--color-tih06-bg: oklch(0.13 0.008 285);
--color-tih06-text: #f1f5f9;
--color-tih06-text: oklch(0.96 0.005 250);
--color-tih06-muted: #475569;
--color-tih06-muted: oklch(0.63 0.03 255);
--color-tih06-acc: #22d3ee;
--color-tih06-acc: oklch(0.79 0.13 205);
--font-tih06-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih06: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-06 {
width: 100%;
min-height: 100svh;
}
.tih-06 ::selection {
background: oklch(0.79 0.13 205);
color: #000;
}
```How this works
The filter stack is composable. This is the property Tailwind models best:
<div class="grayscale brightness-75 saturate-0
transition-[filter,scale] duration-550 ease-tih06
group-hover:grayscale-0 group-hover:brightness-100
group-hover:saturate-120 group-hover:scale-104">Each function is its own utility and Tailwind assembles the filter value, so a partially desaturated resting state is grayscale-60 — one token change, not a rewritten declaration.
The border pseudo-element is gone. The CSS version adds an ::after with a transparent border purely to animate a frame. ring-2 ring-transparent with a group-hover: colour does the same thing on the card itself, with no pseudo-element and no pointer-events: none caveat.
Stacking-context warning, still true. A non-none filter creates a stacking context, so the portrait layer is its own context on hover and z-order against unfiltered siblings can shift. The port keeps the info bar as a later sibling, above it in paint order regardless.
Make it yours
- Partial resting colour:
grayscale-60instead ofgrayscale, per card if you like — it is a per-element utility. - Punch:
group-hover:saturate-150for editorial portraiture;saturate-105for corporate. - Hold before fading back:
delay-75outside thegroup-hover:half applies only on mouse-out. - Frame weight:
group-hover:ring-4plusring-offset-2 ring-offset-tih06-bgfor a gapped frame. - Retheme through
--color-tih06-acc, which paints the ring, the role line and the selection colour.
Gotchas — read before shipping
grayscaleis the bare utility for 100%;grayscale-0is the off switch. Writinggrayscale-100works but reads worse than the bare form.- A filter creates a stacking context. If a filtered card contains a
fixeddescendant, it will be clipped to the card — true in every browser, and not a Tailwind issue. - Name both properties in
transition-[filter,scale]. Firefox drops a frame or two when a filter and a transform start on the same element from separate transition declarations. - Hover-only colour is decorative here; the name and role are always legible. Never let a filter carry meaning.
saturate-120is not on Tailwind's default scale as a “step” — v4 accepts the bare number, which is why the overshoot needs no arbitrary value.- The browser CDN build compiles in the page and is for prototyping only. Ship the CLI or a plugin build.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
filter is unprefixed from Chrome 53, Safari 9.1 and Firefox 35; Tailwind emits no -webkit-filter, so add it manually if you must support Safari 9.