20 CSS Image Hover Effects04 / 20
CSS Grid Image Blur Overlay Text Hover
Visually soft hover where the background image blurs under a dark overlay via backdrop-filter to make typography legible.
Published
The code
<div class="ih-04">
<div class="ih-04__grid">
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--1"></div>
<span class="ih-04__emoji">🌌</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Editorial</p>
<p class="ih-04__title">The Infinite Canvas</p>
<p class="ih-04__desc">Exploring the boundaries of digital art in 2025</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--2"></div>
<span class="ih-04__emoji">🌿</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Nature</p>
<p class="ih-04__title">Bioluminescent Depths</p>
<p class="ih-04__desc">Life thriving in the ocean's twilight zone</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--3"></div>
<span class="ih-04__emoji">✨</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Culture</p>
<p class="ih-04__title">Neon Flux Collection</p>
<p class="ih-04__desc">Street art meets future-forward design aesthetics</p>
</div>
</div>
</div>
</div><div class="ih-04">
<div class="ih-04__grid">
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--1"></div>
<span class="ih-04__emoji">🌌</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Editorial</p>
<p class="ih-04__title">The Infinite Canvas</p>
<p class="ih-04__desc">Exploring the boundaries of digital art in 2025</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--2"></div>
<span class="ih-04__emoji">🌿</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Nature</p>
<p class="ih-04__title">Bioluminescent Depths</p>
<p class="ih-04__desc">Life thriving in the ocean's twilight zone</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--3"></div>
<span class="ih-04__emoji">✨</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Culture</p>
<p class="ih-04__title">Neon Flux Collection</p>
<p class="ih-04__desc">Street art meets future-forward design aesthetics</p>
</div>
</div>
</div>
</div>.ih-04,
.ih-04 *,
.ih-04 *::before,
.ih-04 *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ih-04 ::selection {
background: #8b5cf6;
color: #fff;
}
.ih-04 {
--accent: #8b5cf6;
--bg: #080810;
--text: #f1f5f9;
--muted: #94a3b8;
--duration: 0.4s;
--ease: cubic-bezier(0.25,0.46,0.45,0.94);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 40px 24px;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.ih-04__grid {
display: grid;
grid-template-columns: repeat(3,1fr);
gap: 16px;
max-width: 780px;
width: 100%;
}
.ih-04__card {
position: relative;
border-radius: 14px;
overflow: hidden;
aspect-ratio: 3/4;
cursor: pointer;
}
.ih-04__bg {
position: absolute;
inset: 0;
transition: filter var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__bg--1 {
background: linear-gradient(135deg,#0f0c29,#302b63,#24243e);
}
.ih-04__bg--2 {
background: linear-gradient(135deg,#0a3d2e,#155e43,#6ee7b7);
}
.ih-04__bg--3 {
background: linear-gradient(135deg,#1a0010,#6b1d4e,#e879f9);
}
.ih-04__emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-60%);
font-size: 60px;
opacity: 0.35;
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* backdrop-filter blur on overlay — applied to the ::before pseudo so it blurs the sibling bg */
.ih-04__overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0);
/* We use backdrop-filter on the overlay itself */
backdrop-filter: blur(0px) brightness(1);
-webkit-backdrop-filter: blur(0px) brightness(1);
transition: backdrop-filter var(--duration) var(--ease), -webkit-backdrop-filter var(--duration) var(--ease), background var(--duration) var(--ease);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
}
.ih-04__card:hover .ih-04__bg {
transform: scale(1.05);
}
.ih-04__card:hover .ih-04__emoji {
opacity: 0.08;
transform: translate(-50%,-60%) scale(1.2);
}
.ih-04__card:hover .ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
-webkit-backdrop-filter: blur(8px) brightness(0.6);
background: rgba(0,0,0,0.15);
}
.ih-04__cat {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 6px;
opacity: 0;
transform: translateY(10px);
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__title {
font-size: 15px;
font-weight: 700;
color: var(--text);
line-height: 1.3;
opacity: 0;
transform: translateY(12px);
transition: opacity calc(var(--duration)*1.1) var(--ease), transform calc(var(--duration)*1.1) var(--ease);
}
.ih-04__desc {
font-size: 11px;
color: var(--muted);
margin-top: 5px;
opacity: 0;
transform: translateY(14px);
transition: opacity calc(var(--duration)*1.2) var(--ease), transform calc(var(--duration)*1.2) var(--ease);
}
.ih-04__card:hover .ih-04__cat,
.ih-04__card:hover .ih-04__title,
.ih-04__card:hover .ih-04__desc {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.ih-04__bg,
.ih-04__overlay,
.ih-04__cat,
.ih-04__title,
.ih-04__desc,
.ih-04__emoji {
transition: none;
}
.ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
}
.ih-04__cat,
.ih-04__title,
.ih-04__desc {
opacity: 1;
transform: none;
}
}.ih-04,
.ih-04 *,
.ih-04 *::before,
.ih-04 *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ih-04 ::selection {
background: #8b5cf6;
color: #fff;
}
.ih-04 {
--accent: #8b5cf6;
--bg: #080810;
--text: #f1f5f9;
--muted: #94a3b8;
--duration: 0.4s;
--ease: cubic-bezier(0.25,0.46,0.45,0.94);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 40px 24px;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.ih-04__grid {
display: grid;
grid-template-columns: repeat(3,1fr);
gap: 16px;
max-width: 780px;
width: 100%;
}
.ih-04__card {
position: relative;
border-radius: 14px;
overflow: hidden;
aspect-ratio: 3/4;
cursor: pointer;
}
.ih-04__bg {
position: absolute;
inset: 0;
transition: filter var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__bg--1 {
background: linear-gradient(135deg,#0f0c29,#302b63,#24243e);
}
.ih-04__bg--2 {
background: linear-gradient(135deg,#0a3d2e,#155e43,#6ee7b7);
}
.ih-04__bg--3 {
background: linear-gradient(135deg,#1a0010,#6b1d4e,#e879f9);
}
.ih-04__emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-60%);
font-size: 60px;
opacity: 0.35;
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* backdrop-filter blur on overlay — applied to the ::before pseudo so it blurs the sibling bg */
.ih-04__overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0);
/* We use backdrop-filter on the overlay itself */
backdrop-filter: blur(0px) brightness(1);
-webkit-backdrop-filter: blur(0px) brightness(1);
transition: backdrop-filter var(--duration) var(--ease), -webkit-backdrop-filter var(--duration) var(--ease), background var(--duration) var(--ease);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
}
.ih-04__card:hover .ih-04__bg {
transform: scale(1.05);
}
.ih-04__card:hover .ih-04__emoji {
opacity: 0.08;
transform: translate(-50%,-60%) scale(1.2);
}
.ih-04__card:hover .ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
-webkit-backdrop-filter: blur(8px) brightness(0.6);
background: rgba(0,0,0,0.15);
}
.ih-04__cat {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 6px;
opacity: 0;
transform: translateY(10px);
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__title {
font-size: 15px;
font-weight: 700;
color: var(--text);
line-height: 1.3;
opacity: 0;
transform: translateY(12px);
transition: opacity calc(var(--duration)*1.1) var(--ease), transform calc(var(--duration)*1.1) var(--ease);
}
.ih-04__desc {
font-size: 11px;
color: var(--muted);
margin-top: 5px;
opacity: 0;
transform: translateY(14px);
transition: opacity calc(var(--duration)*1.2) var(--ease), transform calc(var(--duration)*1.2) var(--ease);
}
.ih-04__card:hover .ih-04__cat,
.ih-04__card:hover .ih-04__title,
.ih-04__card:hover .ih-04__desc {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.ih-04__bg,
.ih-04__overlay,
.ih-04__cat,
.ih-04__title,
.ih-04__desc,
.ih-04__emoji {
transition: none;
}
.ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
}
.ih-04__cat,
.ih-04__title,
.ih-04__desc {
opacity: 1;
transform: none;
}
}Here's a working CSS Image Hover Effect 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: CSS Grid Image Blur Overlay Text Hover
Source: https://codefronts.com/motion/css-image-hover-effects/css-grid-image-blur-overlay-text-hover/
Visually soft hover where the background image blurs under a dark overlay via backdrop-filter to make typography legible.
## HTML
```html
<div class="ih-04">
<div class="ih-04__grid">
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--1"></div>
<span class="ih-04__emoji">🌌</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Editorial</p>
<p class="ih-04__title">The Infinite Canvas</p>
<p class="ih-04__desc">Exploring the boundaries of digital art in 2025</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--2"></div>
<span class="ih-04__emoji">🌿</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Nature</p>
<p class="ih-04__title">Bioluminescent Depths</p>
<p class="ih-04__desc">Life thriving in the ocean's twilight zone</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--3"></div>
<span class="ih-04__emoji">✨</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Culture</p>
<p class="ih-04__title">Neon Flux Collection</p>
<p class="ih-04__desc">Street art meets future-forward design aesthetics</p>
</div>
</div>
</div>
</div>
```
## CSS
```css
.ih-04,
.ih-04 *,
.ih-04 *::before,
.ih-04 *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ih-04 ::selection {
background: #8b5cf6;
color: #fff;
}
.ih-04 {
--accent: #8b5cf6;
--bg: #080810;
--text: #f1f5f9;
--muted: #94a3b8;
--duration: 0.4s;
--ease: cubic-bezier(0.25,0.46,0.45,0.94);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 40px 24px;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.ih-04__grid {
display: grid;
grid-template-columns: repeat(3,1fr);
gap: 16px;
max-width: 780px;
width: 100%;
}
.ih-04__card {
position: relative;
border-radius: 14px;
overflow: hidden;
aspect-ratio: 3/4;
cursor: pointer;
}
.ih-04__bg {
position: absolute;
inset: 0;
transition: filter var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__bg--1 {
background: linear-gradient(135deg,#0f0c29,#302b63,#24243e);
}
.ih-04__bg--2 {
background: linear-gradient(135deg,#0a3d2e,#155e43,#6ee7b7);
}
.ih-04__bg--3 {
background: linear-gradient(135deg,#1a0010,#6b1d4e,#e879f9);
}
.ih-04__emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-60%);
font-size: 60px;
opacity: 0.35;
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* backdrop-filter blur on overlay — applied to the ::before pseudo so it blurs the sibling bg */
.ih-04__overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0);
/* We use backdrop-filter on the overlay itself */
backdrop-filter: blur(0px) brightness(1);
-webkit-backdrop-filter: blur(0px) brightness(1);
transition: backdrop-filter var(--duration) var(--ease), -webkit-backdrop-filter var(--duration) var(--ease), background var(--duration) var(--ease);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
}
.ih-04__card:hover .ih-04__bg {
transform: scale(1.05);
}
.ih-04__card:hover .ih-04__emoji {
opacity: 0.08;
transform: translate(-50%,-60%) scale(1.2);
}
.ih-04__card:hover .ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
-webkit-backdrop-filter: blur(8px) brightness(0.6);
background: rgba(0,0,0,0.15);
}
.ih-04__cat {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 6px;
opacity: 0;
transform: translateY(10px);
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__title {
font-size: 15px;
font-weight: 700;
color: var(--text);
line-height: 1.3;
opacity: 0;
transform: translateY(12px);
transition: opacity calc(var(--duration)*1.1) var(--ease), transform calc(var(--duration)*1.1) var(--ease);
}
.ih-04__desc {
font-size: 11px;
color: var(--muted);
margin-top: 5px;
opacity: 0;
transform: translateY(14px);
transition: opacity calc(var(--duration)*1.2) var(--ease), transform calc(var(--duration)*1.2) var(--ease);
}
.ih-04__card:hover .ih-04__cat,
.ih-04__card:hover .ih-04__title,
.ih-04__card:hover .ih-04__desc {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.ih-04__bg,
.ih-04__overlay,
.ih-04__cat,
.ih-04__title,
.ih-04__desc,
.ih-04__emoji {
transition: none;
}
.ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
}
.ih-04__cat,
.ih-04__title,
.ih-04__desc {
opacity: 1;
transform: none;
}
}
```Here's a working CSS Image Hover Effect 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: CSS Grid Image Blur Overlay Text Hover
Source: https://codefronts.com/motion/css-image-hover-effects/css-grid-image-blur-overlay-text-hover/
Visually soft hover where the background image blurs under a dark overlay via backdrop-filter to make typography legible.
## HTML
```html
<div class="ih-04">
<div class="ih-04__grid">
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--1"></div>
<span class="ih-04__emoji">🌌</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Editorial</p>
<p class="ih-04__title">The Infinite Canvas</p>
<p class="ih-04__desc">Exploring the boundaries of digital art in 2025</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--2"></div>
<span class="ih-04__emoji">🌿</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Nature</p>
<p class="ih-04__title">Bioluminescent Depths</p>
<p class="ih-04__desc">Life thriving in the ocean's twilight zone</p>
</div>
</div>
<div class="ih-04__card">
<div class="ih-04__bg ih-04__bg--3"></div>
<span class="ih-04__emoji">✨</span>
<div class="ih-04__overlay">
<p class="ih-04__cat">Culture</p>
<p class="ih-04__title">Neon Flux Collection</p>
<p class="ih-04__desc">Street art meets future-forward design aesthetics</p>
</div>
</div>
</div>
</div>
```
## CSS
```css
.ih-04,
.ih-04 *,
.ih-04 *::before,
.ih-04 *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ih-04 ::selection {
background: #8b5cf6;
color: #fff;
}
.ih-04 {
--accent: #8b5cf6;
--bg: #080810;
--text: #f1f5f9;
--muted: #94a3b8;
--duration: 0.4s;
--ease: cubic-bezier(0.25,0.46,0.45,0.94);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 40px 24px;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.ih-04__grid {
display: grid;
grid-template-columns: repeat(3,1fr);
gap: 16px;
max-width: 780px;
width: 100%;
}
.ih-04__card {
position: relative;
border-radius: 14px;
overflow: hidden;
aspect-ratio: 3/4;
cursor: pointer;
}
.ih-04__bg {
position: absolute;
inset: 0;
transition: filter var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__bg--1 {
background: linear-gradient(135deg,#0f0c29,#302b63,#24243e);
}
.ih-04__bg--2 {
background: linear-gradient(135deg,#0a3d2e,#155e43,#6ee7b7);
}
.ih-04__bg--3 {
background: linear-gradient(135deg,#1a0010,#6b1d4e,#e879f9);
}
.ih-04__emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-60%);
font-size: 60px;
opacity: 0.35;
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* backdrop-filter blur on overlay — applied to the ::before pseudo so it blurs the sibling bg */
.ih-04__overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0);
/* We use backdrop-filter on the overlay itself */
backdrop-filter: blur(0px) brightness(1);
-webkit-backdrop-filter: blur(0px) brightness(1);
transition: backdrop-filter var(--duration) var(--ease), -webkit-backdrop-filter var(--duration) var(--ease), background var(--duration) var(--ease);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
}
.ih-04__card:hover .ih-04__bg {
transform: scale(1.05);
}
.ih-04__card:hover .ih-04__emoji {
opacity: 0.08;
transform: translate(-50%,-60%) scale(1.2);
}
.ih-04__card:hover .ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
-webkit-backdrop-filter: blur(8px) brightness(0.6);
background: rgba(0,0,0,0.15);
}
.ih-04__cat {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 6px;
opacity: 0;
transform: translateY(10px);
transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-04__title {
font-size: 15px;
font-weight: 700;
color: var(--text);
line-height: 1.3;
opacity: 0;
transform: translateY(12px);
transition: opacity calc(var(--duration)*1.1) var(--ease), transform calc(var(--duration)*1.1) var(--ease);
}
.ih-04__desc {
font-size: 11px;
color: var(--muted);
margin-top: 5px;
opacity: 0;
transform: translateY(14px);
transition: opacity calc(var(--duration)*1.2) var(--ease), transform calc(var(--duration)*1.2) var(--ease);
}
.ih-04__card:hover .ih-04__cat,
.ih-04__card:hover .ih-04__title,
.ih-04__card:hover .ih-04__desc {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.ih-04__bg,
.ih-04__overlay,
.ih-04__cat,
.ih-04__title,
.ih-04__desc,
.ih-04__emoji {
transition: none;
}
.ih-04__overlay {
backdrop-filter: blur(8px) brightness(0.6);
}
.ih-04__cat,
.ih-04__title,
.ih-04__desc {
opacity: 1;
transform: none;
}
}
```How this works
backdrop-filter: blur(8px) brightness(0.6) is applied to the overlay element rather than the image itself. This means the blur composites against everything rendered behind the overlay — the image, any gradients, any decorative shapes. On hover, both the blur and brightness values animate from zero using a transition on the backdrop-filter shorthand.
Text children of the overlay start hidden with opacity: 0; transform: translateY(10px) and enter on hover, staggered via duration multipliers. The image simultaneously scales up slightly with transform: scale(1.05) to reinforce that it is "active", while the blur softens it visually to push it to the background.
Make it yours
- Increase the blur radius to
blur(16px)for a more opaque frosted-glass feel, or drop it toblur(4px)for a subtle soft-focus effect. - Tint the overlay with a brand colour:
background: rgba(124, 58, 237, 0.15)gives a violet wash while still showing the image beneath. - Add
saturate(0.5)to thebackdrop-filterstack to desaturate the image as it blurs:backdrop-filter: blur(8px) brightness(0.6) saturate(0.5). - For cards with bright images, increase the brightness reduction:
brightness(0.45)ensures white text remains AA contrast-compliant. - Combine with a
mix-blend-mode: multiplyon the overlay background for more aggressive colour grading effects.
Gotchas — read before shipping
backdrop-filterdoes NOT work on elements that are siblings of the blurred content — it only blurs what is visually behind it in the stacking order. The overlay must be a child or sibling sitting on top of the image layer.- Safari requires the
-webkit-backdrop-filtervendor prefix in addition to the unprefixed version — always include both. - Animating
backdrop-filtercan cause significant GPU memory usage when many cards are visible simultaneously — consider applying only within a visible viewport via IntersectionObserver.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 76+ | 9+ (prefixed) | 103+ | 76+ |
Firefox added backdrop-filter support in v103 without a flag; older Firefox versions will see the text overlay without blur.