20 CSS Image Hover Effects 19 / 20

CSS Image Icon Reveal Center Hover

Thumbnail grid where the image darkens and an action icon (play, magnify, link) scales in at the exact centre on hover using transform origin.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="ih-19">
  <div class="ih-19__grid">
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--1"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🎬</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">▶</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Intro Reel 2025</span><span class="ih-19__views">4.2k views</span></div>
    </div>
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--2"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🌿</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">🔍</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Jungle Macro</span><span class="ih-19__views">1.8k views</span></div>
    </div>
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--3"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🔮</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">🔗</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Prism Study</span><span class="ih-19__views">9.1k views</span></div>
    </div>
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--4"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🌅</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">▶</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Golden Hour BTS</span><span class="ih-19__views">3.3k views</span></div>
    </div>
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--5"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🌊</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">🔍</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Deep Blue Series</span><span class="ih-19__views">7.6k views</span></div>
    </div>
    <div class="ih-19__card">
      <div class="ih-19__img ih-19__img--6"></div>
      <div class="ih-19__img-content"><span class="ih-19__thumb">🌺</span></div>
      <div class="ih-19__icon-wrap"><div class="ih-19__icon-btn">🔗</div></div>
      <div class="ih-19__strip"><span class="ih-19__title">Bloom Editorial</span><span class="ih-19__views">2.9k views</span></div>
    </div>
  </div>
</div>
.ih-19,.ih-19 *,.ih-19 *::before,.ih-19 *::after{margin:0;padding:0;box-sizing:border-box}
.ih-19 ::selection{background:#818cf8;color:#fff}
.ih-19{
  --accent:#818cf8;--bg:#07070e;--text:#f1f5f9;--muted:#475569;
  --duration:0.38s;--ease:cubic-bezier(0.16,1,0.3,1);
  font-family:system-ui,sans-serif;background:var(--bg);padding:40px 24px;
  min-height: 100vh;display:flex;align-items:center;justify-content:center;
}
.ih-19__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;max-width:780px;width:100%}
.ih-19__card{position:relative;border-radius:10px;overflow:hidden;aspect-ratio:4/3;cursor:pointer}

.ih-19__img{position:absolute;inset:0;transition:filter var(--duration) var(--ease),transform var(--duration) var(--ease)}
.ih-19__img--1{background:linear-gradient(135deg,#0f0c29,#312e81,#4338ca)}
.ih-19__img--2{background:linear-gradient(135deg,#042f2e,#065f46,#059669)}
.ih-19__img--3{background:linear-gradient(135deg,#3b0764,#7e22ce,#a855f7)}
.ih-19__img--4{background:linear-gradient(135deg,#1c1003,#92400e,#d97706)}
.ih-19__img--5{background:linear-gradient(135deg,#0c1445,#1e3a8a,#2563eb)}
.ih-19__img--6{background:linear-gradient(135deg,#1a0010,#9d174d,#e11d48)}
.ih-19__card:hover .ih-19__img{filter:brightness(0.4);transform:scale(1.06)}

.ih-19__img-content{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.ih-19__thumb{font-size:40px;opacity:0.4;transition:opacity var(--duration) var(--ease), transform var(--duration) var(--ease)}
.ih-19__card:hover .ih-19__thumb{opacity:0;transform:scale(0.7)}

/* The center icon — scales in from 0 */
.ih-19__icon-wrap{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  opacity:0;transform:scale(0.5) rotate(-20deg);
  transition:opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.ih-19__card:hover .ih-19__icon-wrap{opacity:1;transform:scale(1) rotate(0deg)}

.ih-19__icon-btn{
  width:52px;height:52px;border-radius:50%;
  background:rgba(255,255,255,0.12);
  border:2px solid rgba(255,255,255,0.25);
  backdrop-filter:blur(8px);
  display:flex;align-items:center;justify-content:center;
  font-size:22px;
  transition:background 0.2s ease, transform 0.2s ease;
}
.ih-19__card:hover .ih-19__icon-btn:hover{background:rgba(255,255,255,0.22);transform:scale(1.1)}

/* Bottom strip */
.ih-19__strip{
  position:absolute;bottom:0;left:0;right:0;padding:8px 10px;
  background:linear-gradient(to top,rgba(0,0,0,0.8),transparent);
  display:flex;align-items:center;justify-content:space-between;
}
.ih-19__title{font-size:11px;font-weight:700;color:var(--text)}
.ih-19__views{font-size:10px;color:var(--muted)}

@media(prefers-reduced-motion:reduce){.ih-19__img,.ih-19__thumb,.ih-19__icon-wrap{transition:none}.ih-19__icon-wrap{opacity:1;transform:none}}

How this works

The icon wrapper starts at opacity: 0; transform: scale(0.5) rotate(-20deg) and transitions to natural size on hover. The image element simultaneously receives filter: brightness(0.4) to darken it, providing contrast for the white icon. The icon is centred using position: absolute; inset: 0; display: flex; align-items: center; justify-content: center — no manual offsets needed.

The small rotational component in the starting state (rotate(-20deg)) gives the icon entrance a spinning-in quality that stops exactly at 0 degrees. The spring easing cubic-bezier(0.16, 1, 0.3, 1) combined with scale(0.5) → scale(1) produces an elastic "pop" as the icon materialises at the centre.

Customize

  • Change the icon per card by placing different emoji or SVG in each .icon-btn element — the size, transition, and positioning remain the same.
  • Add a tooltip label below the icon using an absolutely-positioned span that also fades in with a slight delay (transition-delay: 0.1s).
  • For video thumbnails, replace the darkening filter with a subtle blur: filter: blur(2px) brightness(0.7) for a soft depth-of-field focus effect.
  • Add a :hover state on the icon button itself (not just the card) that scales it further to scale(1.12) for an extra micro-interaction layer.
  • Use an SVG icon inside the button instead of emoji for sharper rendering at all DPI levels and easier colour control via fill: currentColor.

Watch out for

  • Setting filter: brightness() on an image that contains position: fixed children will clip those children to the filtered element's bounds — ensure no fixed children exist inside filtered cards.
  • The scale(0.5) rotate(-20deg) starting transform compounds the two functions — the order matters: scale(0.5) rotate(-20deg) is NOT the same as rotate(-20deg) scale(0.5). Always apply scale first.
  • On Firefox, the spring easing cubic-bezier(0.16, 1, 0.3, 1) can slightly overshoot on the icon and cause a brief clip against the card edge — reduce the scale start to 0.6 if this occurs.

Browser support

ChromeSafariFirefoxEdge
51+ 9+ 36+ 51+

Uses only CSS transforms, filter, and opacity — universally supported in all modern browsers without prefixes.

Search CodeFronts

Loading…