30 CSS Hover Effects 24 / 30
CSS Image Tilt Depth Hover Effect
Cards that tilt in 3-D perspective with floating depth layers and dynamic drop-shadows, entirely in CSS.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="hv-24">
<p class="hv-24__label">Image Tilt Depth — 4 Variants</p>
<div class="hv-24__grid">
<div class="hv-24__card hv-24__card--portrait">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt1/300/400" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Portrait Tilt</div>
</div>
</div>
<div class="hv-24__card hv-24__card--landscape">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt2/400/260" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Landscape Tilt</div>
</div>
</div>
<div class="hv-24__card hv-24__card--deep">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt3/300/400" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Deep Perspective</div>
</div>
</div>
<div class="hv-24__card hv-24__card--flat">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt4/400/260" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Flat Hover Lift</div>
</div>
</div>
</div>
</div> <div class="hv-24">
<p class="hv-24__label">Image Tilt Depth — 4 Variants</p>
<div class="hv-24__grid">
<div class="hv-24__card hv-24__card--portrait">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt1/300/400" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Portrait Tilt</div>
</div>
</div>
<div class="hv-24__card hv-24__card--landscape">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt2/400/260" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Landscape Tilt</div>
</div>
</div>
<div class="hv-24__card hv-24__card--deep">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt3/300/400" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Deep Perspective</div>
</div>
</div>
<div class="hv-24__card hv-24__card--flat">
<div class="hv-24__inner">
<img class="hv-24__img" src="https://picsum.photos/seed/tilt4/400/260" alt="" />
<div class="hv-24__shine"></div>
<div class="hv-24__caption">Flat Hover Lift</div>
</div>
</div>
</div>
</div>.hv-24,
.hv-24 *,
.hv-24 *::before,
.hv-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-24 {
font-family: system-ui, sans-serif;
background: #0e0e12;
padding: 2.5rem 2rem;
min-height: 100vh;
}
.hv-24__label {
text-align: center;
color: #666;
font-size: .75rem;
letter-spacing: .14em;
text-transform: uppercase;
margin-bottom: 2.5rem;
}
.hv-24__grid {
display: grid;
grid-template-columns: repeat(2, auto);
gap: 2.5rem;
justify-content: center;
align-items: start;
}
/* ── Card shell — holds perspective ── */
.hv-24__card {
perspective: 700px;
cursor: pointer;
}
/* ── Inner — rotates in 3-D ── */
.hv-24__inner {
position: relative;
border-radius: 12px;
transform-style: preserve-3d;
transition:
transform .45s cubic-bezier(.23,1,.32,1),
box-shadow .45s cubic-bezier(.23,1,.32,1);
box-shadow: 0 8px 30px rgba(0,0,0,.55);
overflow: hidden; /* clip image but we accept safari caveat */
}
.hv-24__card--portrait .hv-24__inner { width: 220px; aspect-ratio: 3/4; }
.hv-24__card--landscape .hv-24__inner { width: 320px; aspect-ratio: 16/9; }
.hv-24__card--deep .hv-24__inner { width: 220px; aspect-ratio: 3/4; }
.hv-24__card--flat .hv-24__inner { width: 320px; aspect-ratio: 16/9; }
/* per-variant tilt angles */
.hv-24__card--portrait:hover .hv-24__inner { transform: rotateY(12deg) rotateX(-6deg); box-shadow: -14px 18px 50px rgba(0,0,0,.7); }
.hv-24__card--landscape:hover .hv-24__inner { transform: rotateX(10deg) rotateY(-5deg); box-shadow: 0 -14px 40px rgba(0,0,0,.6); }
.hv-24__card--deep:hover .hv-24__inner { transform: rotateY(-16deg) rotateX(8deg); box-shadow: 20px 14px 60px rgba(0,0,0,.8); }
.hv-24__card--flat:hover .hv-24__inner { transform: rotateX(5deg) rotateY(5deg) translateZ(12px); box-shadow: -8px 20px 50px rgba(0,0,0,.65); }
.hv-24__img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* ── Specular shine overlay ── */
.hv-24__shine {
position: absolute;
inset: 0;
background: conic-gradient(from 135deg at 50% 0%,
rgba(255,255,255,.18) 0deg,
transparent 80deg);
opacity: 0;
transition: opacity .4s;
pointer-events: none;
border-radius: inherit;
}
.hv-24__card:hover .hv-24__shine { opacity: 1; }
/* ── Caption ── */
.hv-24__caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: .65rem .9rem;
background: linear-gradient(transparent, rgba(0,0,0,.75));
color: #fff;
font-size: .78rem;
letter-spacing: .06em;
transform: translateY(4px);
opacity: 0;
transition: opacity .35s, transform .35s;
pointer-events: none;
}
.hv-24__card:hover .hv-24__caption { opacity: 1; transform: translateY(0); }
@media (max-width: 560px) {
.hv-24__grid { grid-template-columns: 1fr; }
.hv-24__card--portrait .hv-24__inner,
.hv-24__card--deep .hv-24__inner { width: 100%; }
.hv-24__card--landscape .hv-24__inner,
.hv-24__card--flat .hv-24__inner { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
.hv-24__inner { transition: none !important; }
.hv-24__shine, .hv-24__caption { transition: none !important; }
} .hv-24,
.hv-24 *,
.hv-24 *::before,
.hv-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-24 {
font-family: system-ui, sans-serif;
background: #0e0e12;
padding: 2.5rem 2rem;
min-height: 100vh;
}
.hv-24__label {
text-align: center;
color: #666;
font-size: .75rem;
letter-spacing: .14em;
text-transform: uppercase;
margin-bottom: 2.5rem;
}
.hv-24__grid {
display: grid;
grid-template-columns: repeat(2, auto);
gap: 2.5rem;
justify-content: center;
align-items: start;
}
/* ── Card shell — holds perspective ── */
.hv-24__card {
perspective: 700px;
cursor: pointer;
}
/* ── Inner — rotates in 3-D ── */
.hv-24__inner {
position: relative;
border-radius: 12px;
transform-style: preserve-3d;
transition:
transform .45s cubic-bezier(.23,1,.32,1),
box-shadow .45s cubic-bezier(.23,1,.32,1);
box-shadow: 0 8px 30px rgba(0,0,0,.55);
overflow: hidden; /* clip image but we accept safari caveat */
}
.hv-24__card--portrait .hv-24__inner { width: 220px; aspect-ratio: 3/4; }
.hv-24__card--landscape .hv-24__inner { width: 320px; aspect-ratio: 16/9; }
.hv-24__card--deep .hv-24__inner { width: 220px; aspect-ratio: 3/4; }
.hv-24__card--flat .hv-24__inner { width: 320px; aspect-ratio: 16/9; }
/* per-variant tilt angles */
.hv-24__card--portrait:hover .hv-24__inner { transform: rotateY(12deg) rotateX(-6deg); box-shadow: -14px 18px 50px rgba(0,0,0,.7); }
.hv-24__card--landscape:hover .hv-24__inner { transform: rotateX(10deg) rotateY(-5deg); box-shadow: 0 -14px 40px rgba(0,0,0,.6); }
.hv-24__card--deep:hover .hv-24__inner { transform: rotateY(-16deg) rotateX(8deg); box-shadow: 20px 14px 60px rgba(0,0,0,.8); }
.hv-24__card--flat:hover .hv-24__inner { transform: rotateX(5deg) rotateY(5deg) translateZ(12px); box-shadow: -8px 20px 50px rgba(0,0,0,.65); }
.hv-24__img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* ── Specular shine overlay ── */
.hv-24__shine {
position: absolute;
inset: 0;
background: conic-gradient(from 135deg at 50% 0%,
rgba(255,255,255,.18) 0deg,
transparent 80deg);
opacity: 0;
transition: opacity .4s;
pointer-events: none;
border-radius: inherit;
}
.hv-24__card:hover .hv-24__shine { opacity: 1; }
/* ── Caption ── */
.hv-24__caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: .65rem .9rem;
background: linear-gradient(transparent, rgba(0,0,0,.75));
color: #fff;
font-size: .78rem;
letter-spacing: .06em;
transform: translateY(4px);
opacity: 0;
transition: opacity .35s, transform .35s;
pointer-events: none;
}
.hv-24__card:hover .hv-24__caption { opacity: 1; transform: translateY(0); }
@media (max-width: 560px) {
.hv-24__grid { grid-template-columns: 1fr; }
.hv-24__card--portrait .hv-24__inner,
.hv-24__card--deep .hv-24__inner { width: 100%; }
.hv-24__card--landscape .hv-24__inner,
.hv-24__card--flat .hv-24__inner { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
.hv-24__inner { transition: none !important; }
.hv-24__shine, .hv-24__caption { transition: none !important; }
}How this works
The card uses CSS perspective + rotateX/rotateY on :hover. Inner layers (image, shine overlay, caption) each carry a different translateZ, creating parallax depth. A conic-gradient pseudo-element slides across as a specular highlight.
Customize
- Increase --tilt-depth for more dramatic perspective, adjust --tilt-angle for rotation magnitude, add more translateZ layers for richer depth stack.
Watch out for
- transform-style: preserve-3d must be set on every intermediate container, not just the outermost wrapper.
- overflow: hidden on a preserve-3d element collapses the 3-D context in Safari — use a sibling overlay instead.
- The shine pseudo-element must be pointer-events: none or it swallows hover events on the caption.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ |
CSS 3-D transforms fully supported everywhere.
More from 30 CSS Hover Effects
CSS Slide Reveal Card Hover EffectCSS Stack Lift Card Hover EffectCSS Zoom Pan Image Hover EffectCSS Color Channel Split Hover EffectCSS Duotone Image Hover EffectCSS Curtain Reveal Image Hover EffectCSS Ken Burns Image Hover EffectCSS Underline Slide Nav Hover EffectCSS Highlight Fill Nav Hover EffectCSS Strikethrough Hover Link EffectCSS Inline Word Swap Hover EffectCSS Dot Trail Cursor Hover Effect
View the full collection →