Pure CSSMIT licensed

3D Flip Cards (Front to Back)

A team-member profile card that rotates a full 180° on the Y-axis using perspective and backface-visibility, flipping from a photo-and-name front to a back face with bio and social links. Space-efficient and tactile, it suits team pages, flashcard apps, pricing tiers, and anywhere two sides of information share one footprint.

Live Demo
Plain CSSTailwind v4
Try it

Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS

ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<div class="card-04">
  <article class="card-04__card">
    <div class="card-04__face card-04__front">
      <span class="card-04__ff-role">Lead Designer</span>
      <h2 class="card-04__ff-name">Mara<br>Okonkwo</h2>
      <span class="card-04__ff-hint">↻ hover to flip</span>
    </div>
    <div class="card-04__face card-04__back">
      <div>
        <span class="card-04__fb-role">Lead Designer</span>
        <h3 class="card-04__fb-name">Mara Okonkwo</h3>
        <p class="card-04__fb-bio">Twelve years shaping product systems for fintech and health. Obsessed with motion, type, and the space between clicks.</p>
      </div>
      <div class="card-04__fb-links">
        <a href="#">Email</a>
        <a href="#">Dribbble</a>
        <a href="#">LinkedIn</a>
      </div>
    </div>
  </article>
</div>
@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=DM+Sans:opsz,wght@9..40,400;9..40,500&display=swap');

.card-04,
.card-04 *,
.card-04 *::before,
.card-04 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.card-04 {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #eceae3;
  background-image: radial-gradient(circle at 70% 20%, #f6f4ee, #dcd9cf);
  font-family: 'DM Sans', sans-serif;
  padding: 2rem;
  perspective: 1600px;
}

.card-04__card {
  width: 320px;
  height: 430px;
  cursor: pointer;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.75s cubic-bezier(0.4,0.15,0.2,1);
}

.card-04__card:hover {
  transform: rotateY(180deg);
}

.card-04__face {
  position: absolute;
  inset: 0;
  border-radius: 24px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  box-shadow: 0 20px 45px rgba(60,55,40,0.2);
}
/* FRONT — photo + name */

.card-04__front {
  background: linear-gradient(165deg, #2f9e8f 0%, #145c52 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 2.2rem;
  color: #f2fbf9;
}

.card-04__front::before {
  content: "";
  position: absolute;
  top: -10%;
  left: 50%;
  width: 200px;
  height: 200px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #d8f5ee, #6cc4b4);
  box-shadow: 0 20px 50px rgba(0,0,0,0.2);
}

.card-04__ff-role {
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #aef0e3;
}

.card-04__ff-name {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 800;
  font-size: 2.2rem;
  line-height: 1;
  margin-top: 0.3rem;
}

.card-04__ff-hint {
  margin-top: 1rem;
  font-size: 0.74rem;
  color: #cdeee7;
  opacity: 0.8;
}
/* BACK — bio + links */

.card-04__back {
  background: linear-gradient(165deg, #1c1b1a, #0f0e0d);
  transform: rotateY(180deg);
  padding: 2.2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #ece9e2;
}

.card-04__fb-role {
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #5fd3c0;
}

.card-04__fb-name {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 800;
  font-size: 1.7rem;
  line-height: 1.05;
  margin: 0.3rem 0 0.9rem;
}

.card-04__fb-bio {
  font-size: 0.85rem;
  line-height: 1.6;
  color: #b3afa6;
}

.card-04__fb-links {
  display: flex;
  gap: 0.6rem;
}

.card-04__fb-links a {
  flex: 1;
  text-align: center;
  text-decoration: none;
  font-size: 0.78rem;
  font-weight: 500;
  color: #ece9e2;
  padding: 0.6rem;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.18);
  transition: background 0.3s ease;
}

.card-04__fb-links a:hover {
  background: rgba(95,211,192,0.18);
}

@media (prefers-reduced-motion: reduce) {
  .card-04__card,
    .card-04__fb-links a {
    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 Card 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: 3D Flip Cards (Front to Back)
Source: https://codefronts.com/motion/css-card-hover-effects/3d-flip-cards-front-to-back/

A team-member profile card that rotates a full 180° on the Y-axis using perspective and backface-visibility, flipping from a photo-and-name front to a back face with bio and social links. Space-efficient and tactile, it suits team pages, flashcard apps, pricing tiers, and anywhere two sides of information share one footprint.
## HTML
```html
<div class="card-04">
  <article class="card-04__card">
    <div class="card-04__face card-04__front">
      <span class="card-04__ff-role">Lead Designer</span>
      <h2 class="card-04__ff-name">Mara<br>Okonkwo</h2>
      <span class="card-04__ff-hint">↻ hover to flip</span>
    </div>
    <div class="card-04__face card-04__back">
      <div>
        <span class="card-04__fb-role">Lead Designer</span>
        <h3 class="card-04__fb-name">Mara Okonkwo</h3>
        <p class="card-04__fb-bio">Twelve years shaping product systems for fintech and health. Obsessed with motion, type, and the space between clicks.</p>
      </div>
      <div class="card-04__fb-links">
        <a href="#">Email</a>
        <a href="#">Dribbble</a>
        <a href="#">LinkedIn</a>
      </div>
    </div>
  </article>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=DM+Sans:opsz,wght@9..40,400;9..40,500&display=swap');

.card-04,
.card-04 *,
.card-04 *::before,
.card-04 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.card-04 {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #eceae3;
  background-image: radial-gradient(circle at 70% 20%, #f6f4ee, #dcd9cf);
  font-family: 'DM Sans', sans-serif;
  padding: 2rem;
  perspective: 1600px;
}

.card-04__card {
  width: 320px;
  height: 430px;
  cursor: pointer;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.75s cubic-bezier(0.4,0.15,0.2,1);
}

.card-04__card:hover {
  transform: rotateY(180deg);
}

.card-04__face {
  position: absolute;
  inset: 0;
  border-radius: 24px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  box-shadow: 0 20px 45px rgba(60,55,40,0.2);
}
/* FRONT — photo + name */

.card-04__front {
  background: linear-gradient(165deg, #2f9e8f 0%, #145c52 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 2.2rem;
  color: #f2fbf9;
}

.card-04__front::before {
  content: "";
  position: absolute;
  top: -10%;
  left: 50%;
  width: 200px;
  height: 200px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #d8f5ee, #6cc4b4);
  box-shadow: 0 20px 50px rgba(0,0,0,0.2);
}

.card-04__ff-role {
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #aef0e3;
}

.card-04__ff-name {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 800;
  font-size: 2.2rem;
  line-height: 1;
  margin-top: 0.3rem;
}

.card-04__ff-hint {
  margin-top: 1rem;
  font-size: 0.74rem;
  color: #cdeee7;
  opacity: 0.8;
}
/* BACK — bio + links */

.card-04__back {
  background: linear-gradient(165deg, #1c1b1a, #0f0e0d);
  transform: rotateY(180deg);
  padding: 2.2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #ece9e2;
}

.card-04__fb-role {
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #5fd3c0;
}

.card-04__fb-name {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 800;
  font-size: 1.7rem;
  line-height: 1.05;
  margin: 0.3rem 0 0.9rem;
}

.card-04__fb-bio {
  font-size: 0.85rem;
  line-height: 1.6;
  color: #b3afa6;
}

.card-04__fb-links {
  display: flex;
  gap: 0.6rem;
}

.card-04__fb-links a {
  flex: 1;
  text-align: center;
  text-decoration: none;
  font-size: 0.78rem;
  font-weight: 500;
  color: #ece9e2;
  padding: 0.6rem;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.18);
  transition: background 0.3s ease;
}

.card-04__fb-links a:hover {
  background: rgba(95,211,192,0.18);
}

@media (prefers-reduced-motion: reduce) {
  .card-04__card,
    .card-04__fb-links a {
    transition: none !important;
  }
}
```

How this works

Two faces (.card-04__front, .card-04__back) share the same absolute position inside a parent that has perspective: 1000px. Both faces get backface-visibility: hidden so whichever side is turned away from the viewer becomes invisible instead of showing through as a mirror image.

The card itself sets transform-style: preserve-3d so its children render in real 3D space. The back face is pre-rotated by rotateY(180deg) so at rest it faces away.

On :hover (and :focus-within for keyboard users), the card rotates by rotateY(180deg) — the front face now faces away and hides, the back rotates into view. One shared transition animates the whole flip.

Make it yours

  • Flip on the X-axis instead of Y — swap every rotateY(180deg) for rotateX(180deg) for a horizontal-hinge card.
  • Change flip duration on the shared transition — 400ms for snappy, 700ms for luxurious.
  • Rebrand front and back independently from --front-bg, --front-fg, --back-bg, --back-fg on .card-04.
  • Add a subtle 2D scale(1.02) at 50% of the flip to give the card a physical &ldquo;lift&rdquo; feel during rotation.
  • For team-profile pattern, put photo + name on front and bio + social links on back; for product pattern, put product photo on front and price + specs on back.

Gotchas — read before shipping

  • backface-visibility: hidden must be set on BOTH faces, not just the back — Safari 14- occasionally paints a &ldquo;ghosted&rdquo; back-through-front without it.
  • The parent needs the perspective, not the card itself — perspective on the flipping element flattens the rotation into a 2D skew.
  • Use :focus-within alongside :hover so keyboard users can Tab into the card and see the back — hover-only would exclude them entirely.
  • Interactive elements on the back face need pointer-events: none at rest so they don't intercept clicks meant for the front; add pointer events back when .card-04:hover triggers.
  • Under prefers-reduced-motion, replace the flip with a fade-cross between the two faces to preserve the information without spinning the card.

Browser support

ChromeSafariFirefoxEdge
36+9+16+36+

CSS 3D transforms, preserve-3d, perspective and backface-visibility are universally supported. :focus-within for keyboard equivalence is Chrome 60+, Safari 10.1+, Firefox 52+.

Techniques used in this demo

Search CodeFronts

Loading…