30 CSS Hover Effects24 / 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.
Published
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;
}
}Here's a working CSS 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 Image Tilt Depth Hover Effect
Source: https://codefronts.com/motion/css-hover-effects/css-image-tilt-depth-hover-effect/
Cards that tilt in 3-D perspective with floating depth layers and dynamic drop-shadows, entirely in CSS.
## HTML
```html
<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>
```
## CSS
```css
.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;
}
}
```Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS 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 Image Tilt Depth Hover Effect
Source: https://codefronts.com/motion/css-hover-effects/css-image-tilt-depth-hover-effect/
Cards that tilt in 3-D perspective with floating depth layers and dynamic drop-shadows, entirely in CSS.
## HTML
```html
<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>
```
## CSS
```css
.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.Make it yours
- Increase --tilt-depth for more dramatic perspective, adjust --tilt-angle for rotation magnitude, add more translateZ layers for richer depth stack.
Gotchas — read before shipping
- 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.