Flipping Off CSS and JavaScript

HTML/CSS, Javascript, programming

I asked CoPilot in Microsoft Edge to help me get started with something in a Web page to allow me flip between cards:

You are an expert in Javascript and CSS.

Generate the code in HTML, Javascript, and CSS to animate flipping a picture along the X-axis and then revealing a different picture.

Honestly I didn’t know how well that was going to turn out. Surprisingly, very well. It generated these:

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Flip Animation</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="flip-container" onclick="flipImage()">
        <div class="flipper">
            <img src="image1.jpg" alt="Image 1" class="front">
            <img src="image2.jpg" alt="Image 2" class="back">
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f0f0f0;
    margin: 0;
}

.flip-container {
    perspective: 1000px;
}

.flipper {
    position: relative;
    width: 300px;
    height: 200px;
    transform-style: preserve-3d;
    transition: transform 0.6s;
}

.flip-container.flipped .flipper {
    transform: rotateX(180deg);
}

.front, .back {
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
}

.front {
    z-index: 2;
    transform: rotateX(0deg);
}

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

JavaScript:

function flipImage() {
    const container = document.querySelector('.flip-container');
    container.classList.toggle('flipped');
}

I plugged in two JPGs I had lying around, and it did work quite well.

With that, I tweaked it to fit more what I had in mind.

HTML:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Flip Animation</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="flip-container">
        <div class="flip-element">
            <div class="textcard-container front">
                <div class="title">FRONT</div>
                <div class="body">This is the front card</div>
            </div>
            <div class="textcard-container back">
                <div class="title">BACK</div>
                <div class="body">
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque imperdiet libero eu neque facilisis, ac pretium nisi dignissim. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla.
                </div>
            </div>
        </div>
    </div>
    <div id="flipper">
        <button onclick="flipImage()">Flip</button>
    </div>
    <script src="script.js"></script>
</body>
</html>

CSS

.flip-container {
    perspective: 1000px;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 80vh;
    background-color: #f0f0f0;
    margin: 0;    
}

.flip-element {
    position: relative;
    width: 300px;
    height: 200px;
    transform-style: preserve-3d;
    transition: transform 0.3s;
}

.flip-container.flipped .flip-element {
    transform: rotateX(180deg);
}

.front, .back {
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
}

.front {
    z-index: 2;
    transform: rotateX(0deg);
    background-color: cornsilk;
}

.back {
    transform: rotateX(180deg);
    background-color: white;
}

.textcard-container {
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    text-align: center;
}

.textcard-container .title {
    justify-self: flex-start;
    width: 100%;
    box-sizing: border-box;
}

.textcard-container .body {
    justify-self: flex-end;
    box-sizing: border-box;
    padding: 15px;
    overflow: scroll;
}

.textcard-container .body:focus {
    outline-color: cornsilk;
}

#flipper {
    width: 100%;
    text-align: center;
    margin-top: 15px;
}

The JavaScript remains unchanged for now.

Looking at the various pieces, here are the notes:

Stacking Two Elements

The first thing is stacking the two cards with the “front” card on top of the “back” card. This is done by:

  1. Use the same dimensions for two DIVs (cards) inside a parent DIV (container).
  2. Add transform-style: preserve-3d for the container so that whatever animation we apply will stay.
  3. Add a transition: transform 0.3s (or however long desired for the animation sequence) to the container as well.
  4. Use absolute positioning for the cards so that they overlap.
  5. Give the front card a z-index of 2 (anything higher than that of the back card).
  6. Set the backface-visibility property to hidden. This ensures that cards flipped will be hidden.
  7. Rotate the back card by 180 degrees. This makes the back card invisible for now. It’s strictly not necessary since it’s obscured by the front card. However, this will be important when we do the flip.

Flip Animation

Set a CSS rule “flipped” that, when applied to the container, will rotate both child cards 180 degrees. This will:

  1. Flip the front card 180 degrees, and because we have backface-visibility: hidden, the front card will now be invisible (even though its z-index is still higher than that of the back card).
  2. Flip the back card 180 degrees. Since the back card was initially flipped 180 degrees, another 180 degrees now flips it to be front-facing. And since it’s now no longer flipped, it will be visible!
  3. In summary, when we flip both cards, we make the front card invisible and the back card visible.
    • If we didn’t need to animate, then theoretically we could leave the back card alone (visible) and just flip the front card.
    • However, because we ARE animating the flip, leaving the back card stationary will have it show through as the front card rotates in and out of visibility. Therefore, flipping both cards will give the smooth illusion that we are flipping a two-faced card with the front and back cards’ contents. Sneaky, isn’t it?

Now it just relies on toggling the CSS class “flipped” on the container. And that’s where the JavaScript comes in.

I didn’t know that JavaScript now can toggle a class for an element easily using the {element}.classList.toggle({class name}) function. I used to have to split the class list into an array and then iterate through the array to either add or skip and then setting the class list to the resulting list. This was a nice surprise.

Perspective

Setting perspective: 1000px on the container adds a nice touch that, when flipping, there’s a small 3-D effect. Though it’s something I’d miss if I blinked.