Most demos need a newer browser

They use :has() and container queries. Update your browser, or on an iPhone update iOS to 16.4 or later.

CSS 3D Card Flip

CSS card flip built from perspective, transform-style, backface-visibility and a rotateY transform. Hover versions on both axes plus a click variant.

Level
Intermediate
Published
Updated
Reading time
6 min

Try it

3 live demos

🎨 Front Side
Hover to flip
✨ Back Side
Pure CSS
Card Two
Hover me
No JS needed!

Flip on hover

Other ways to build it

Flip on click, for touch and keyboard

See the code

Build it

6 snippets to copy

CSS 3D Card Flip

HTML

<div class="flip-card">
  <div class="flip-inner">
    <div class="flip-front">🎨 Front Side</div>
    <div class="flip-back">✨ Back Side</div>
  </div>
</div>

CSS

.flip-card {
  width: 200px;
  height: 130px;
  perspective: 800px;
}

.flip-inner {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  transition: transform .55s ease;
}

.flip-card:hover .flip-inner {
  transform: rotateY(180deg);
}

.flip-front,
.flip-back {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  backface-visibility: hidden;
  display: flex;
  /* stacked, so a <br> between title and sub label breaks */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  text-align: center;
  font-size: .95rem;
  font-weight: 600;
}

.flip-front {
  background: #b8ff57;
  color: #0c0c0d;
}

/* Pre-rotated so it faces the viewer once the card turns */
.flip-back {
  background: #c084fc;
  color: #0c0c0d;
  transform: rotateY(180deg);
}

Vertical flip

Rotating on the X axis turns the card top over bottom instead of left to right. Only two values change, and they have to change together: the hover rule and the pre rotation on the back face both move from rotateY to rotateX. Leave one behind and the back face arrives upside down.

HTML

<div class="flip-card flip-vertical">
  <div class="flip-inner">
    <div class="flip-front">Front<br><small style="font-weight:400;opacity:.8">Hover to flip</small></div>
    <div class="flip-back">Back<br><small style="font-weight:400;opacity:.8">rotateX</small></div>
  </div>
</div>

CSS

.flip-card:hover .flip-inner {
  transform: rotateX(180deg);
}

.flip-back {
  transform: rotateX(180deg);
}

Flip on click, for touch and keyboard

A hidden checkbox holds the open state and a label wraps the card, so tapping or pressing space turns it over. This is the version to reach for when the back face carries content the reader needs, because hover reaches neither touchscreens nor keyboards. The focus ring sits on the label, since the checkbox driving it is visually hidden.

HTML

<div class="flip-card">
  <input type="checkbox" id="card-1" class="flip-toggle">
  <label for="card-1" class="flip-click">
    <div class="flip-inner">
      <div class="flip-front">Tap to flip</div>
      <div class="flip-back">Back face</div>
    </div>
  </label>
</div>

CSS

.flip-toggle {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.flip-click {
  display: block;
  /* the label sits between the sized card and .flip-inner, so it has
     to carry the box or the inner height: 100% resolves to zero */
  width: 100%;
  height: 100%;
  cursor: pointer;
}

.flip-toggle:checked + .flip-click .flip-inner {
  transform: rotateY(180deg);
}

/* the checkbox is hidden, so the focus ring goes on the label */
.flip-toggle:focus-visible + .flip-click .flip-inner {
  outline: 2px solid #b8ff57;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .flip-inner { transition: none; }
}

Understand it

How it works and 5 properties

A CSS card flip turns a panel over to reveal a second face on its back, and it needs four properties to work: perspective, transform-style, backface-visibility, and a rotateY transform. No JavaScript is involved. The browser handles the rotation on the compositor, so the animation stays smooth even on a page that is busy doing other work.

The pattern shows up on pricing tables, team photos, product tiles, and flashcards. Below are three working versions: the standard horizontal flip on hover, a vertical flip on the X axis, and a click driven flip that works on phones and with a keyboard. Each one is the same technique with a different trigger.

How it works

The container sets perspective to create the 3D space. The inner element holds both faces and uses transform-style:preserve-3d so children exist in the same 3D plane. The back face is pre-rotated 180deg and both faces have backface-visibility:hidden so the correct face shows at each point of the flip.

Three elements do the work. The outer .flip-card is a plain box that never moves. Its only job is perspective, which tells the browser how far the viewer is from the screen. A small value like 400px gives a dramatic, almost fisheye rotation. A large value like 2000px flattens the effect until it barely reads as 3D at all. The 800px used here sits in the middle.

The middle element is the one that rotates. It carries transform-style: preserve-3d, without which the browser flattens its children into a single plane and the back face never appears. This is the property people most often leave out, and the symptom is a card that fades or squashes rather than turning.

The two faces are stacked on top of each other with position: absolute and inset: 0. The back face is rotated 180 degrees in the stylesheet, before any interaction, so that when the parent turns 180 degrees the back arrives facing the viewer the right way round. backface-visibility: hidden hides whichever face is currently pointing away, which is what stops the two from showing through each other mid turn.

Because the whole effect is one transform, the browser can run it on the compositor thread rather than recalculating layout. That is why the flip stays smooth where an equivalent effect built from width and height changes would stutter.

Accessibility notes

A hover flip is unreachable for anyone using a keyboard, and on a touchscreen it either does nothing or fires on the first tap in a way people do not expect. If the back face holds information the reader needs, use the click driven version below, which is a checkbox and a label, so it is focusable and operable with the space bar.

Content on the back face stays in the accessibility tree even while it is turned away, because backface-visibility only affects painting. When the back is purely decorative, mark it aria-hidden="true". When it holds real content, that exposure is usually what you want.

The stylesheet includes a prefers-reduced-motion block that removes the transition. The card still changes face, it stops spinning to get there. Rotation is a common migraine and nausea trigger, and honoring that setting costs three lines.

Mistakes worth avoiding

  • Leaving transform-style: preserve-3d off the rotating element. The card appears to squash instead of turn, which is the single most common reason a flip looks wrong.
  • Putting perspective on the same element that rotates. It belongs on the parent. On the rotating element it produces a much weaker effect that changes as the card turns.
  • Forgetting to pre rotate the back face. The back arrives mirrored, and any text on it reads backwards.
  • Giving the outer card no fixed height. Both faces are absolutely positioned and therefore contribute nothing to the parent's height, so the card collapses to zero.
  • Relying on hover alone. Roughly half of all traffic is touch, and those readers never see the back.

Ship it

3 features in 4 browsers, 5 questions

Browser support

Feature Chrome Firefox Safari Edge
transform-style1210412
backface-visibility1210412
transition455.112

Support figures come from Can I Use for CSS 3D Transforms and CSS Transitions. Every browser in current use handles this, so no fallback is required. Safari has historically needed -webkit-backface-visibility on older versions, though not on any release from the last several years.

What you can build with it

  • Pricing tiers. Plan name and price on the front, the full feature list on the back, which keeps a comparison table short without hiding the detail behind a separate page.
  • Team pages. A photo on the front and a short bio with contact links on the back.
  • Flashcards. Question on the front, answer on the back. The click version is the right trigger here, since the reader should decide when to reveal.
  • Product tiles. Product shot on the front, specifications or a size chart on the back.
  • Statistic cards. A headline number on the front and the source or methodology behind it on the back.

Frequently asked questions

Does a CSS card flip work on mobile?
The transform does, but the hover trigger does not. Mobile browsers fake a hover on first tap, so the card flips and then usually flips back as soon as the reader touches anything else. The click driven variant above uses a checkbox instead and behaves correctly on touch.
Why is my card squashing instead of flipping?
The rotating element is missing transform-style: preserve-3d. Without it the browser flattens both faces into one plane, so instead of turning through 3D space the card scales horizontally down to nothing and back.
Can I flip a card vertically?
Yes, a card can flip vertically. Swap rotateY for rotateX on both the hover rule and the pre rotated back face. Both values have to change together, otherwise the back face arrives upside down.
Is a hover flip accessible?
Not on its own. Keyboard users cannot trigger it. Use the checkbox version when the back face carries content that matters, and respect prefers-reduced-motion.
Does this need any JavaScript?
No, the card flip needs no JavaScript. Every version on this page is HTML and CSS. The click driven one uses a hidden checkbox and a label, which is a standard technique for holding state in CSS alone.