30 CSS Hover Effects13 / 30

Pure CSSMIT licensed

CSS 3D Tilt Card Hover Effect

Four card hover effects with perspective-based 3D depth — static tilt corner, perspective flip-axis, floating lift with shadow depth, and layered parallax layers — using CSS perspective, rotateX/Y transforms, and transform-style: preserve-3d.

Published

Live Demo
Try it

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-13">
  <div class="hv-13__grid">
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--tilt">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">◈</div>
          <h3 class="hv-13__title">Static Tilt</h3>
          <p class="hv-13__text">Perspective rotateX + rotateY on hover</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--flip">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">⬡</div>
          <h3 class="hv-13__title">Axis Flip</h3>
          <p class="hv-13__text">rotateY 180° perspective reveal</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene hv-13__scene--lift">
      <div class="hv-13__card hv-13__card--lift">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">▲</div>
          <h3 class="hv-13__title">Float Lift</h3>
          <p class="hv-13__text">translateY + shadow depth</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--layers">
        <div class="hv-13__layer hv-13__layer--bg"></div>
        <div class="hv-13__layer hv-13__layer--mid">
          <div class="hv-13__icon">◉</div>
        </div>
        <div class="hv-13__layer hv-13__layer--top">
          <h3 class="hv-13__title">Parallax</h3>
          <p class="hv-13__text">translateZ layered depth</p>
        </div>
      </div>
    </div>
  </div>
</div>
.hv-13,
.hv-13 *,
.hv-13 *::before,
.hv-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.hv-13 ::selection {
  background: #0284c7;
  color: #fff;
}

.hv-13 {
  --bg: #020810;
  --text: #bae6fd;
  --dim: #475569;
  --blue: #0ea5e9;
  --sky: #38bdf8;
  --indigo: #6366f1;
  --teal: #14b8a6;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
}

.hv-13__grid {
  display: grid;
  grid-template-columns: repeat(2,1fr);
  gap: 40px;
  max-width: 760px;
  width: 100%;
}

.hv-13__scene {
  perspective: 800px;
}

.hv-13__card {
  width: 100%;
  min-height: 220px;
  border-radius: 16px;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.1);
  transition: transform .4s cubic-bezier(.4,0,.2,1),box-shadow .4s;
  transform-style: preserve-3d;
  cursor: pointer;
}

.hv-13__card-body {
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.hv-13__icon {
  font-size: 2rem;
  color: var(--blue);
}

.hv-13__title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
}

.hv-13__text {
  font-size: .85rem;
  color: var(--dim);
  line-height: 1.5;
}
/* 1 — static tilt */

.hv-13__card--tilt:hover {
  transform: rotateX(8deg) rotateY(-8deg);
  box-shadow: 8px 12px 40px rgba(14,165,233,.3);
}
/* 2 — axis flip */

.hv-13__card--flip {
  border-color: rgba(99,102,241,.3);
}

.hv-13__card--flip .hv-13__icon {
  color: var(--indigo);
}

.hv-13__card--flip:hover {
  transform: rotateX(-6deg) rotateY(10deg);
  box-shadow: -10px 8px 40px rgba(99,102,241,.3);
  border-color: rgba(99,102,241,.6);
}
/* 3 — float lift */

.hv-13__card--lift {
  border-color: rgba(20,184,166,.3);
  box-shadow: 0 4px 20px rgba(0,0,0,.4);
  transition: transform .4s cubic-bezier(.4,0,.2,1),box-shadow .4s;
}

.hv-13__card--lift .hv-13__icon {
  color: var(--teal);
}

.hv-13__card--lift:hover {
  transform: translateY(-14px);
  box-shadow: 0 28px 60px rgba(0,0,0,.5),0 0 30px rgba(20,184,166,.2);
}
/* 4 — parallax layers */

.hv-13__card--layers {
  position: relative;
  min-height: 220px;
  overflow: visible;
  transition: transform .4s cubic-bezier(.4,0,.2,1);
  border-color: rgba(56,189,248,.3);
}

.hv-13__card--layers:hover {
  transform: rotateX(6deg) rotateY(-6deg);
}

.hv-13__layer {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  transition: transform .4s cubic-bezier(.4,0,.2,1);
}

.hv-13__layer--bg {
  background: linear-gradient(135deg,rgba(14,165,233,.1),rgba(56,189,248,.05));
}

.hv-13__layer--mid {
  display: flex;
  align-items: flex-start;
  padding: 32px;
}

.hv-13__layer--mid .hv-13__icon {
  color: var(--sky);
  font-size: 2.5rem;
}

.hv-13__card--layers:hover .hv-13__layer--mid {
  transform: translateZ(20px) translateY(-6px);
}

.hv-13__layer--top {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 28px;
  gap: 8px;
}

.hv-13__card--layers:hover .hv-13__layer--top {
  transform: translateZ(35px) translateY(-4px);
}

@media (max-width: 520px) {
  .hv-13__grid {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hv-13__card,
    .hv-13__layer {
    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 3D Tilt Card Hover Effect
Source: https://codefronts.com/motion/css-hover-effects/css-3d-tilt-card-hover-effect/

Four card hover effects with perspective-based 3D depth — static tilt corner, perspective flip-axis, floating lift with shadow depth, and layered parallax layers — using CSS perspective, rotateX/Y transforms, and transform-style: preserve-3d.
## HTML
```html
<div class="hv-13">
  <div class="hv-13__grid">
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--tilt">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">◈</div>
          <h3 class="hv-13__title">Static Tilt</h3>
          <p class="hv-13__text">Perspective rotateX + rotateY on hover</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--flip">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">⬡</div>
          <h3 class="hv-13__title">Axis Flip</h3>
          <p class="hv-13__text">rotateY 180° perspective reveal</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene hv-13__scene--lift">
      <div class="hv-13__card hv-13__card--lift">
        <div class="hv-13__card-body">
          <div class="hv-13__icon">▲</div>
          <h3 class="hv-13__title">Float Lift</h3>
          <p class="hv-13__text">translateY + shadow depth</p>
        </div>
      </div>
    </div>
    <div class="hv-13__scene">
      <div class="hv-13__card hv-13__card--layers">
        <div class="hv-13__layer hv-13__layer--bg"></div>
        <div class="hv-13__layer hv-13__layer--mid">
          <div class="hv-13__icon">◉</div>
        </div>
        <div class="hv-13__layer hv-13__layer--top">
          <h3 class="hv-13__title">Parallax</h3>
          <p class="hv-13__text">translateZ layered depth</p>
        </div>
      </div>
    </div>
  </div>
</div>
```
## CSS
```css
.hv-13,
.hv-13 *,
.hv-13 *::before,
.hv-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.hv-13 ::selection {
  background: #0284c7;
  color: #fff;
}

.hv-13 {
  --bg: #020810;
  --text: #bae6fd;
  --dim: #475569;
  --blue: #0ea5e9;
  --sky: #38bdf8;
  --indigo: #6366f1;
  --teal: #14b8a6;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
}

.hv-13__grid {
  display: grid;
  grid-template-columns: repeat(2,1fr);
  gap: 40px;
  max-width: 760px;
  width: 100%;
}

.hv-13__scene {
  perspective: 800px;
}

.hv-13__card {
  width: 100%;
  min-height: 220px;
  border-radius: 16px;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.1);
  transition: transform .4s cubic-bezier(.4,0,.2,1),box-shadow .4s;
  transform-style: preserve-3d;
  cursor: pointer;
}

.hv-13__card-body {
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.hv-13__icon {
  font-size: 2rem;
  color: var(--blue);
}

.hv-13__title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
}

.hv-13__text {
  font-size: .85rem;
  color: var(--dim);
  line-height: 1.5;
}
/* 1 — static tilt */

.hv-13__card--tilt:hover {
  transform: rotateX(8deg) rotateY(-8deg);
  box-shadow: 8px 12px 40px rgba(14,165,233,.3);
}
/* 2 — axis flip */

.hv-13__card--flip {
  border-color: rgba(99,102,241,.3);
}

.hv-13__card--flip .hv-13__icon {
  color: var(--indigo);
}

.hv-13__card--flip:hover {
  transform: rotateX(-6deg) rotateY(10deg);
  box-shadow: -10px 8px 40px rgba(99,102,241,.3);
  border-color: rgba(99,102,241,.6);
}
/* 3 — float lift */

.hv-13__card--lift {
  border-color: rgba(20,184,166,.3);
  box-shadow: 0 4px 20px rgba(0,0,0,.4);
  transition: transform .4s cubic-bezier(.4,0,.2,1),box-shadow .4s;
}

.hv-13__card--lift .hv-13__icon {
  color: var(--teal);
}

.hv-13__card--lift:hover {
  transform: translateY(-14px);
  box-shadow: 0 28px 60px rgba(0,0,0,.5),0 0 30px rgba(20,184,166,.2);
}
/* 4 — parallax layers */

.hv-13__card--layers {
  position: relative;
  min-height: 220px;
  overflow: visible;
  transition: transform .4s cubic-bezier(.4,0,.2,1);
  border-color: rgba(56,189,248,.3);
}

.hv-13__card--layers:hover {
  transform: rotateX(6deg) rotateY(-6deg);
}

.hv-13__layer {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  transition: transform .4s cubic-bezier(.4,0,.2,1);
}

.hv-13__layer--bg {
  background: linear-gradient(135deg,rgba(14,165,233,.1),rgba(56,189,248,.05));
}

.hv-13__layer--mid {
  display: flex;
  align-items: flex-start;
  padding: 32px;
}

.hv-13__layer--mid .hv-13__icon {
  color: var(--sky);
  font-size: 2.5rem;
}

.hv-13__card--layers:hover .hv-13__layer--mid {
  transform: translateZ(20px) translateY(-6px);
}

.hv-13__layer--top {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 28px;
  gap: 8px;
}

.hv-13__card--layers:hover .hv-13__layer--top {
  transform: translateZ(35px) translateY(-4px);
}

@media (max-width: 520px) {
  .hv-13__grid {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hv-13__card,
    .hv-13__layer {
    transition: none!important;
  }
}
```

How this works

CSS 3D tilt requires three elements working together: the wrapper sets perspective: 800px to establish a vanishing point, the card uses transform-style: preserve-3d so child layers exist in the same 3D space, and the hover state applies rotateX(8deg) rotateY(-8deg) to tilt the face. The fixed angle approach (no JS cursor tracking) achieves a convincing 3D still by choosing a consistent diagonal tilt that creates depth without requiring mouse position data.

The floating lift variant doesn't rotate at all — instead it uses translateY(-12px) combined with a larger spread box-shadow (0 24px 60px rgba(0,0,0,.5)) whose offset grows as the card rises, simulating a real object casting a longer shadow as it moves away from the surface. The parallax layer card uses translateZ() on child elements to offset them at different depths, creating a layered parallax that the perspective makes visible even without cursor tracking.

Make it yours

  • Steepen the tilt by increasing from rotateX(8deg) to rotateX(14deg) — values above 20deg look exaggerated and break readable text.
  • Change the perspective distance with perspective: 600px for a more extreme fish-eye effect or perspective: 1200px for a subtle, almost-flat tilt.
  • Add a specular highlight with a ::before gradient overlay that also rotates on hover, simulating light reflecting off the tilted card face.
  • Combine with a filter: drop-shadow() instead of box-shadow — drop-shadow follows the card's transformed outline for a more accurate 3D cast shadow.
  • Add a transition-duration: .1s on hover and .4s on mouse-out for an asymmetric fast-tilt / slow-return feel.

Gotchas — read before shipping

  • transform-style: preserve-3d is ignored on elements with overflow: hidden — do not combine these properties or child 3D transforms will be flattened.
  • The card border and background must be on the same element — applying a border-radius parent with perspective and a child with transform can cause Safari to render the radius incorrectly in 3D.
  • will-change: transform on the tilt card pre-promotes it to a GPU layer — useful for performance but can cause z-index stacking context issues with overlapping siblings.

Browser support

ChromeSafariFirefoxEdge
60+12+60+60+

transform-style: preserve-3d is fully supported. Safari has minor quirks with overflow+3d combinations.

Techniques used in this demo

3D transforms (perspective + preserve-3d)grid-templateMDN ↗

Search CodeFronts

Loading…