20 CSS Image Hover Effects 08 / 20

Neumorphic Image Card Hover Effect CSS

Soft-UI cards that shift from an extruded dual-shadow state to an inset "pressed" shadow on hover, mimicking tactile depth.

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

The code

<div class="ih-08">
  <div class="ih-08__grid">
    <div class="ih-08__card">
      <div class="ih-08__img-frame">
        <div class="ih-08__img ih-08__img--1"><span class="ih-08__icon">🎮</span></div>
      </div>
      <div class="ih-08__body">
        <p class="ih-08__title">Phantom Controller</p>
        <p class="ih-08__sub">Haptic · Wireless · 40h</p>
        <div class="ih-08__foot">
          <span class="ih-08__price">$89</span>
          <button class="ih-08__btn">Add to Cart</button>
        </div>
      </div>
    </div>
    <div class="ih-08__card">
      <div class="ih-08__img-frame">
        <div class="ih-08__img ih-08__img--2"><span class="ih-08__icon">🎧</span></div>
      </div>
      <div class="ih-08__body">
        <p class="ih-08__title">AquaTone Studio</p>
        <p class="ih-08__sub">ANC · Hi-Res · 60h</p>
        <div class="ih-08__foot">
          <span class="ih-08__price">$229</span>
          <button class="ih-08__btn">Add to Cart</button>
        </div>
      </div>
    </div>
    <div class="ih-08__card">
      <div class="ih-08__img-frame">
        <div class="ih-08__img ih-08__img--3"><span class="ih-08__icon">💻</span></div>
      </div>
      <div class="ih-08__body">
        <p class="ih-08__title">NovaPad Ultra</p>
        <p class="ih-08__sub">OLED · 12-core · 18h</p>
        <div class="ih-08__foot">
          <span class="ih-08__price">$1,299</span>
          <button class="ih-08__btn">Add to Cart</button>
        </div>
      </div>
    </div>
  </div>
</div>
.ih-08,.ih-08 *,.ih-08 *::before,.ih-08 *::after{margin:0;padding:0;box-sizing:border-box}
.ih-08 ::selection{background:#6366f1;color:#fff}
.ih-08{
  --nm-bg:#1e1e2a;--nm-light:rgba(255,255,255,0.05);--nm-dark:rgba(0,0,0,0.45);
  --accent:#6366f1;--accent2:#22d3ee;--text:#e2e8f0;--muted:#64748b;
  --duration:0.4s;--ease:cubic-bezier(0.25,0.46,0.45,0.94);
  font-family:system-ui,sans-serif;background:var(--nm-bg);padding:40px 24px;
  min-height: 100vh;display:flex;align-items:center;justify-content:center;
}
.ih-08__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;max-width:780px;width:100%}
/* Neumorphic base: dual shadow (light top-left, dark bottom-right) */
.ih-08__card{
  background:var(--nm-bg);border-radius:20px;padding:12px;cursor:pointer;
  box-shadow:6px 6px 16px var(--nm-dark), -4px -4px 12px var(--nm-light);
  transition:box-shadow var(--duration) var(--ease), transform var(--duration) var(--ease);
}
/* Hover: inset shadow = pressed state */
.ih-08__card:hover{
  box-shadow:inset 4px 4px 10px var(--nm-dark), inset -3px -3px 8px var(--nm-light);
  transform:scale(0.98);
}
.ih-08__img-frame{
  border-radius:12px;overflow:hidden;aspect-ratio:1;position:relative;
  box-shadow:inset 2px 2px 6px var(--nm-dark),inset -1px -1px 4px var(--nm-light);
}
.ih-08__img{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:transform var(--duration) var(--ease)}
.ih-08__img--1{background:linear-gradient(135deg,#1e1b4b,#312e81,#4338ca)}
.ih-08__img--2{background:linear-gradient(135deg,#0c4a6e,#075985,#38bdf8)}
.ih-08__img--3{background:linear-gradient(135deg,#3b0764,#6b21a8,#a855f7)}
.ih-08__card:hover .ih-08__img{transform:scale(1.04)}
.ih-08__icon{font-size:48px;opacity:0.45}
.ih-08__body{padding:12px 4px 4px}
.ih-08__title{font-size:13px;font-weight:700;color:var(--text);margin-bottom:3px}
.ih-08__sub{font-size:11px;color:var(--muted)}
.ih-08__foot{display:flex;align-items:center;justify-content:space-between;margin-top:10px}
.ih-08__price{font-size:15px;font-weight:700;color:var(--accent)}
/* Neumorphic pill button */
.ih-08__btn{
  padding:5px 12px;border-radius:20px;font-size:10px;font-weight:700;
  color:var(--text);letter-spacing:0.06em;
  background:var(--nm-bg);
  box-shadow:3px 3px 8px var(--nm-dark), -2px -2px 6px var(--nm-light);
  transition:box-shadow 0.2s ease;border:none;cursor:pointer;
}
.ih-08__btn:hover{box-shadow:inset 2px 2px 6px var(--nm-dark),inset -1px -1px 4px var(--nm-light)}
@media(prefers-reduced-motion:reduce){.ih-08__card,.ih-08__img{transition:none}}

How this works

Neumorphism relies on two carefully offset box-shadow values: a light shadow at the top-left (-4px -4px 12px rgba(255,255,255,0.05)) and a dark shadow at the bottom-right (6px 6px 16px rgba(0,0,0,0.45)). Together these simulate a raised surface lit from the top-left. On hover, both shadows are replaced by inset equivalents with the inset keyword, which pushes the light and dark to the inside of the element, simulating a pressed-in state.

The image frame uses the reverse shadow pair (inset dark + inset light) at rest to create a recessed well effect, which holds the image as if it were behind glass. A slight scale(0.98) on the card body reinforces the pressed feeling during hover.

Customize

  • Match --nm-bg exactly to the page background colour — even a 1-unit mismatch will break the illusion.
  • Adjust shadow intensity by modifying the alpha values: lower the dark shadow to rgba(0,0,0,0.3) for a more subtle effect on lighter backgrounds.
  • Use transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1) for a slight overshoot spring effect on press, matching native button tactile feel.
  • For coloured neumorphism, tint the light shadow with a desaturated variant of the surface colour instead of pure white.
  • Add a :focus-visible ring with a thin outline: 2px solid var(--accent) so keyboard users have a clear focus indicator outside the shadow system.

Watch out for

  • The neumorphic effect only works when the card background exactly matches the page background — it breaks completely if placed on a different-coloured parent.
  • Do NOT use neumorphism on components that must meet WCAG contrast requirements without a visible border or high-contrast mode — the shadow-only affordance is invisible to users with low contrast sensitivity.
  • Combining box-shadow with border-radius in Safari can clip the inner shadow to the wrong inset position below version 14 — add a 1px solid border at 0 opacity as a stability fix.

Browser support

ChromeSafariFirefoxEdge
10+ 5.1+ 4+ 10+

box-shadow with inset keyword is universally supported; the visual output quality depends on the display calibration and ambient lighting.

Search CodeFronts

Loading…