Skip to content

3D Aces

2025.06.14#showcase

How about some dual sided elements that make use of backface-visibilty to help acheive realistic 3D effects ? Let's dive into how this ace of spades gets generated ♠️

A
A

Full demo on Codepen

HTML Setup

Nothing we need to do semantically here, it does happen to be a bunch of <divs/> but the class values will help us make sense of what's what.

html
<div class="card-container">
  <div class="card">
    <div class="front">
      <div class="index">A</div>
      <div class="face">♠</div>
      <div class="index flipped">A</div>
    </div>
    <div class="back"></div>
  </div>
</div>

🔑 points:

  • The actual element that rotates in this setup is the .card element with the .card-container element helping to set up a perspective that makes the .card element rotation look visibly correct in 3D space. More details in the next section.

Container holds it all together

css
@import url("https://fonts.googleapis.com/css2?family=Playfair...");

.card-container {
  font-family: "Playfair", serif;
  perspective: 1000px;
  .../* additional font related styles */;
}
  • Playfair is a google font that looks very "card-like", so we import that for use.
  • perspective is key here because it makes sure the 3D transformations happen at a good distance away from your eyes. For example, if we set it to 100px it will look like the 3D movement gets too close to your eyes ("screen") and things 'stretch'
A
A

Keeping everything centered

We use modern centering techniques with CSS grid on several elements to make sure everything looks like one solid object

css
.card,
.face {
  display: grid;
  place-content: center;
}
  • .card element will contain the front and back of the card
  • .face element will have the index of the card along with the big spade displyed in the center

Card element

css
.card {
  width: 10rem;
  height: 14rem;
  transform-style: preserve-3d;
  animation: flip 5s linear infinite;
  .../* we nest more rules in here */;
}

🔑 points:

  • width and height here give a size to give the card realistic looking proportions.
  • transform-style: preserve-3d is probably the most important part of this whole setup. It 'preserves' any 3D transformations done to child elements. Without it 3D transformations on .front and .back elements wouldn't actually look correct or work entirely.
  • The animation here will keep the card rotating 360 degress infinitely.

Front and Back

Note: We use CSS nesting (did you know you can already nest native CSS ?) with the & symbol which represents the .card element in which the .front and .back elements exist in.

css
& .front,
& .back {
  grid-area: 1 / 1;
  width: inherit;
  height: inherit;
  backface-visibility: hidden;

  border-radius: 0.5rem;
  box-shadow: 1rem 1rem 2rem hsla(0, 0%, 0%, 0.2);
}
  • Because our .card is using display:grid setting grid-area: 1/1 on both the .front and .back keeps these items inside the same cell on top of each other. We are going to flip the .back element though so you don't see both .front and .back at the same time.
  • To acheive this effect of hiding the back side of the card we use backface-visibility: hidden. Anytime during the rotation when the backside of something is displayed you'll see "nothing".
  • border-radius gives us nice rounded corners to the card.

To demonstrate the backface-visibility on the left we have the .front element using a value of visible so that when it rotates around you see it 'backwards' in a mirror-image manner. On the right, the value is hidden to demonstrate how the whole element 'dissapears'. We hide the whole .back element of .card in this example so it doesn't appear during the demonstration.

A
A
A
A

Front of the card (face)

For the face of the card we still need a separate css grid to hold the top-left and bottom-right 'A♠️' text and the large ♠️ in the center. Let's do that with CSS grid.

css
& .front {
  display: grid;
  grid-template-rows: 2rem 1fr 2rem;
  transform: rotateZ(36deg);
  background-color: white;
  color: black;
}

& .index.flipped {
  transform: rotate(180deg);
}
  • grid-tempalate-rows portions the card with enough space for the face values and the center spade ♠️.
  • transform tips the card over to the side enough that it will eventually looks like its spinning on its corner.
  • We give it a solid background-color so that nothing else bleeds through.
  • The 'A♠️' is represented by the .index element and here we see we add one more class .flipped to express that the bottom right one needs to flip 180deg to turn upside down and in the correct corner.

Without the rotateZ transformation this is what a plain looking card looks like.

A
A

Set up the back

While the back looks more complex, it actually a lot easier to setup since it only one element.

css
& .back {
  transform: rotateZ(36deg) rotateY(180deg);
  border: 0.2rem solid darkred;

  background: linear-gradient(
      45deg,
      red 25.5%,
      transparent 25%,
      transparent 75%,
      red 75%
    ), linear-gradient(
      -45deg,
      red 25.5%,
      transparent 25%,
      transparent 75%,
      red 75%
    ), white;

  background-size: 2rem 2rem;
}
  • transform has Z rotatation so that like the .front element it tilts over onto its corner. The Y rotation makes it so that we only see front face first (by 'hiding' the back) and during the .card animation it then flips this backside toward the viewer.
  • border is just a bit darker shade of red to distinguish it from the background pattern generated.
  • background and background-size set up a pattern very similar to what happens in the Animated Checkered Background post but with some modifications to match what some card decks use.
A
A

All together

And now we enable all the pieces together and get this neat effect that could be applied to many more 'cards' 😄

A
A

-zan0