DEV Community

Cover image for Multi-faced Flip Card with a Click (Part 2: CSS)
Maria del Carmen Santiago
Maria del Carmen Santiago

Posted on • Updated on

Multi-faced Flip Card with a Click (Part 2: CSS)

More than just your average Flip Card tutorial. Don’t just flip your card on hover, use JavaScript to flip it on command. Includes instructions on how to change the reverse face to show a different face each time and tricks to give the card a nice 3D effect while flipping.

This series will be divided into three parts (plus a fun bonus at the end):


Part II: The CSS

I will be covering this part by starting from the card outline and working my way in. There will be some explanation about what each declaration is doing, and I will provide links for those of you that want to understand each property further.

In case you feel like you need to review your CSS basics, click here.

The Container

Let’s start with the .card__container selector, which refers to the outline <div> of our card, and give it the desired width and height. You can choose different CSS units for this. I used absolute units by setting both the width and the height to 350px, but you can use relative units (e.g. em, %, vw, vh) if it suits you best for responsiveness.

The last property is a new one for me, at least until a week ago, called perspective. As I mentioned in Part I, the container will stay static and the content will be the one doing the flip. By adding a perspective property to the container of our card we will be giving the child element, which in our case will be the .card__content, a perspective view. As for the property value, a lower number will give you a more intense 3D effect. (Once the project is up and running, try changing the perspective from 350px to 50px and see how freaky it looks. For now, you won’t notice anything though so just keep it in mind for later).

.card__container {
  width: 350px;
  height: 350px;
  perspective: 350px;
}

The Content

With width and height both set to 100%, our .card__content will take up all the space of the parent element, which is that of the .card__container that we just set up a moment ago. The position property will be set to relative.

The transform-style property set to preserve-3d does exactly as it states. This will preserve the 3D appearance of the element as it flips. This effect is only appreciated when applying the transform property, which we will be adding to the .card__content.is-flipped selector. (This new .is-flipped class will be of use to us on the next part of the series.)

You could set the transform property to rotate 180 degrees on the y-axis alone, but adding a few extra things, like translateX(-100%) alongside the transform-origin property will give the flip a nice effect, although this is optional.

The transform-origin default value is 50% 50%, which is the center of the element. When the transformation is applied to the element and the transform-origin is set to center right the rotation’s axis will move to the center of the right-hand edge. You can better understand this concept if you play with the values and see the effects yourself.

The transform-style and transform-origin properties only work when you have a transform property on the element. On the other hand, the transition property works on any property. This is used to allow a smooth transition when there is a change detected in the property selected. In our case we set the transition a value to transform 2s. This means that when the transform property changes it won’t do so abruptly, but will do so over two seconds. You can change this, but I found that 1 second was too quick, and although 3 seconds was acceptable, it was a bit too slow for my taste.

.card__content {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform-origin: center right;
  transition: transform 2s;
}

.card__content.is-flipped {
  transform: translateX(-100%) rotateY(-180deg);
}

The Faces

Now, let’s move on to the content on the front and back of the card. Most of the properties will be the same for these two, except for a few differences. We can group the equal declarations under a class name like I did with .card-face, or you could skip adding that extra class to those <div>s and group the front and back selectors with a comma at the center like so .card-front, .card-back{ property: property-value; }. The choice is yours.

I won’t explain those declarations that are for aesthetics purposes, but I will explain how the other ones will help us get our flip card working. We previously set the position of the .card__content to relative, and that had to do with the next property we will be adding to the face of the card. With a declaration of position: absolute the reverse face of the card will be revealed in the same position as the front face of the card, and not below as the space the two elements take up will be one on top of the other.

This next part is a little difficult to explain, but bear with me. Take two pieces of paper (one to represent the .card-front and another to represent the .card-back of the card) and have them both facing you. When you simulate the flip action, what do you get? You get the back of the .card-back. That is not what we want, which is why we will initially rotate .card-back 180 degrees (or half a turn) on the y-axis. Do the same to your piece of paper and try again.

One of the tricks to getting the reverse face to show different results is to set all of the .card-backs to display: none; and using JavaScript to change that declaration to display: block; the one we want (you’ll see more about this on Part III of the series).

Now, the .card-back of the card won’t always be the face of the card facing backwards, which is why we add the backface-visibility: hidden; declaration to both faces of the card. This means that whichever face is at the back will be hidden and won’t ruin the aesthetic of the face that is showing.

.card__face {   /* -same as- .card-front, .card-back { */
  position: absolute;
  width: 100%;
  height: 100%;   
  background: rgba(10,10,10,0.8);   /* aesthetic  */
  box-shadow: 0 0 10px 8px rgba(255,0,0,0.75);   /* aesthetic  */
  border-radius: 5px;   /* aesthetic  */
  text-align: center;    /* aesthetic  */
  backface-visibility: hidden;
}

.card__back {
  transform: rotateY(180deg);   /* -same as- transform: rotateY(0.5turn); */
  display: none;
}

.card__back.display {
  display: block;
}

The Front of the Card (optional)

This next part you can change to whatever you want. Since the following is all about the aesthetics of the front of the card I won’t explain it, but I will explain a little bit about the selectors used.

When I use something like .card__front > h2 this means that the ruleset that follows will only be applied to the <h2> tags where the parent is .card__front class. When using something like #input__form:hover this means that the ruleset will be applied when the user is hovering over the element with the id of #input__form. That :hover is a CSS pseudo class, just like .btn:active where the ruleset will be applied for selected links with a .btn class.

If you have any questions I do encourage you to comment down below and I will get back to you.

FYI: Selectors starting with # are ids and selectors starting with . are classes. Also, remember that ids are unique to one element.

HTML:

<!--       Front of the card -->
<div class="card__face card__front">
    <h2>Planck's Constant</h2>
    <form>
       <input 
               id="input__form"
               type="number" 
               step="0.00000001" 
               min="6" max="7" 
               placeholder="Enter Number" 
               required="required" />
       <button class="btn" type="submit">Validate</button>
    </form>
</div>

CSS:

.card__front {
  padding: 40px 20px;
}

.card__front > h2 {
  text-align: center;
  margin: 0;
  font-weight: 900;
  padding-bottom: 0.5em;
  color: white;
  text-shadow: -0.05rem -0.05rem 5px rgba(237, 43, 18, 1);
  font-size: 2rem; 
}

.card__front > form {
  margin-top: 25px;
}

.card__front > form input {   <!-- -same as-  #input__form { -->
  padding: 10px;
  border: 1px solid rgba(0,0,0,0.1);
  background-color: #ddd;
  box-shadow: 0 0 5px 3px rgba(255,0,0,0.4);
  border-radius: 5px;
}

.card__front > form input:hover {    <!-- -same as-  #input__form:hover { -->
  background-color: #eee;
  box-shadow: 0 0 7px 5px rgba(255,0,0,0.4);
}

.btn {
  margin: 5px;
  padding: 10px;
  outline: 0;
  border: 1px solid rgba(0,0,0,0.1);
  background-color: transparent;
  box-shadow: 0 0 5px 3px rgba(255,0,0,0.4);
  color: white;
  border-radius: 10px;
}

.btn:hover {
  background-color: #000;
  box-shadow: 0 0 7px 5px rgba(255,0,0,0.4);
}

.btn:active {
  background-color: #333;
}

The result for the front of the card should look something like this:

chrome-JGf-Qsmld-DA.png

The Back of the Card (optional)

Same as the previous section, I won’t explain the code below but if you have any questions feel free to comment down below and I will get back to you on that.

Do keep in mind that in this case, the 3 options we have for the back of the card have the same layout. I only changed the src of the image and the text of the button, but this doesn’t have to be the case. You can add an id to each face of the card and have completely different card layouts for each of them.

For example, I have a #card__back__one > p selector because I have a paragraph that I needed to style on #card__back__one that does not exist on #card__back__two nor on #card__back__three. Remember that each one is literally a completely different face so you can do with them what you want.

HTML:

<!--       Back of card__back__one if 6.62607015 -->
      <div id="card__back__one" class="card__face card__back">
        <img 
             class="image__back" 
             src="https://i.postimg.cc/BZpQPT4M/stranger-things-lights.jpg" 
             alt="Kareen Wheeler happy"/>
        <p>You Got It!</p>
        <button id="btn__back__one" class='btn btn__back'>Reset</button>
      </div>
<!--       Back of card__back__two if 6.62607004 -->
      <div id="card__back__two" class="card__face card__back">
        <img 
             class="image__back" 
             src="https://i.postimg.cc/hjrgxnWf/susie-constante-planck.jpg" 
             alt="Susie's Planck's Constant is 6.62607004."/>
        <button id="btn__back__two" class='btn btn__back'>Reset</button>
      </div> 
<!--       Back of card__back__three if default -->
      <div id="card__back__three" class="card__face card__back">
        <img 
             class="image__back" 
             src="https://i.postimg.cc/tTTkpd57/robin-stranger-things-you-suck.jpg" 
             alt="Robin thinks you suck."/>
        <button id="btn__back__three" class='btn btn__back'>Try Again</button>
      </div> 

CSS:

.image__back {
  width: 100%;
  height: 100%;
  margin: 0px;
  padding: 2px;
  box-shadow: 0 0 0 1px rgba(255,0,0,0.55);
  border-radius: 5px;
}

#card__back__one > p {
  color: #111;
  text-shadow: -0.05rem -0.05rem 5px rgba(237, 43, 18, 1);
  font-size: 2em;
  font-weight: 700;
  text-align: center;
  margin-top: -50px;
}

.btn__back {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(10,10,10,0.8);
}

The result for the back of the card (in this case, of #card__back__three) should look something like this:

chrome-WTf-ECM5ol-I.png

Extra Asethetic Features

I know there are certain things (like the Stranger Things logo) that I didn’t mention in this part, but I will be adding the codepen with the whole project on Part III so you can take a look at those bits that have nothing to do with the actual action of flipping the card in the next part of this series.

Remember to comment below if you have any questions or suggestions. In the next part of this series, we will be looking into the JavaScript of this project.

💻 article.close()

Resources

Top comments (0)