Pure CSS Flip Cards using Bootstrap 4 and CSS Grid [No JS]


Pure CSS Flip Cards using Bootstrap 4 and CSS Grid [No JS]
Last Updated: Feb 19, 2020

Nicolas Kadis |

The Problem

I've wanted to create flipping cards for my website that would resize on different screen sizes using Bootstrap 4's .card class. At first I wanted to find a solution that was purely HTML and CSS. So I started searching online for some answers, but almost all of them lead to either having to set a fixed width, fixed height or too much JavaScript (or even combinations).

I finally realised that I had to combine some of the features on all the code I had seen to make it work. After a lot of tinkering around with bits of code, I came across a video about CSS Grid by Morten Rand-Hendriksen. After watching it I realised that I could use CSS Grid to stack two objects in the same location and use some basic transformations to create my Flipping Cards.

The Solution

The most important part of creating a flipping object is the structure of your HTML. You need:

  • A container to hold the two cards
  • A flipper to carry out the transformation (rotate 180 degrees)
  • A container for the front side
  • A container for the back side

So your structure should look something like this:

<div class="card-container">
    <div class="card-flip">
        <div class="front">
            <!-- card content -->
        </div>
        <div class="back">
            <!-- card content -->
        </div>
    </div>
</div>

Now that we have the basic HTML needed for our flipping cards, we need the appropriate CSS to make the content flip:

.card-container {
    perspective: 700px;
}

.card-flip, .card-container {
    transform-style: preserve-3d;
    transition: all 0.7s ease;
}

.card-flip div {
    backface-visibility: hidden;
    transform-style: preserve-3d;
}

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

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

To break this down:

  • perspective is used to allow a 3D effect for our transformation.
  • transform-style: preserve-3d keeps the 3D effect during transformations.
  • transition eases the transformation effect to make it look nicer.
  • backface-visibility: hidden just hides the back sides of each card.
  • transform is used to:
    1. rotate the back side by 180 degrees so that when the front side is shown, the back side is hidden.
    2. rotate the flipper by 180 degrees so that both cards inside the container flip by 180 degrees.

Now the problem is that our cards do not stack up. This is where CSS Grid comes in handy!

To make our cards stack up, we need to make some small additions to our CSS:

.card-flip {
    display: grid; grid-template: 1fr / 1fr;
    grid-template-areas: "frontAndBack";
    transform-style: preserve-3d;
    transition: all 0.7s ease;
}

.front {
    grid-area: frontAndBack;
}

.back {
    grid-area: frontAndBack;
    transform: rotateY(-180deg);
}

What we just did was introduce CSS Grid to our cards. This being broken down:

  • display: grid on the card flipper allows to use CSS Grid to set positions for the contents of that container
  • grid-template: 1fr/1fr instructs our grid to have a 1 X 1 layout
  • grid-template-areas is used to name the areas of your template for convenience purposes(this is not necessarily needed)
  • grid-area defines the location of the cards(this is what finally stacks our cards on top of each other, since we placed them on the same area)

Now to combine Bootstrap 4 and our Flipping Cards container, we need to adjust our HTML:

<div class="container">
    <div class="row">
        <div class="col-md-4 card-container">
            <div class="card-flip">
                <div class="card front">
                    <!-- card content -->
                </div>
                <div class=" card back">
                    <!-- card content -->
                </div>
            </div>
        </div>
    </div>
</div>

By creating a .row and .col-md-* we can place as many Flipping Cards as we want, allowing them to flow on different rows and adjust on different screen sizes.

The .card is used to make our front and back sides look like cards. Then we can place contents inside the cards as we would usually do with normal cards.

Note: Since there are two cards in the same location, Card Overlay Images might not work exactly as intended. i.e. if the front of your card is an overlay and the contents of the back card are larger than the contents of the front, the height of both cards will be the height of the back side, meaning that the image will cover only the front side's original area.

Now to make our cards have the same height on larger devices, we need to make our .card-container use Grid as well:

.card-container {
    display: grid;
    perspective: 700px;
}

This wraps it all up! Hope you enjoyed this post. Please leave any suggestions or comments below.

See full code on CodePen.

Comments


  • Date T
    16 Jun 2020

    Thanks for sharing, just what I wanted!

  • Lili
    28 Jul 2020

    Thank you ! Very useful.

  • DJ
    11 Sep 2020

    Hey there, Nicolas, I love this elegant and super simple solution but when I went to implement it, I see the back of the card in resting state (prior to hovering) - in Mozilla Firefox only. I tried a few tricks suggested on StackOverflow before bothering you; adding a `transform: rotateY(0deg)` to the .front div, and adding -moz-backface-visibility: hidden; to the parent .card-flip div but nothing seemed to fix it. If you want to check it out, its about 2/3 the way down this page: https://kbmax.com/response?ind=heavy-equipment&amp;uc=engineer-to-order. It's the leanest solution I've found for this, just stuck on this last little thing...Thanks!

    • Nicolas Kadis
      14 Sep 2020

      Hi there DJ, I'm glad you found my solution useful. I've looked at your implementation and I would suggest to add a .card to the .front and .back divs, and also setting the z-index of .front to 1 and the z-index of .back to 0 (or any index you like keeping in mind that .back has to be a lower number. I hope you find this helpful :)

      • DJ
        23 Sep 2020

        Hey again, Nicolas, thanks for taking a look. Adding .card to .front and .back seemed to do the trick. I didn't seem to need the z-index. Thanks so much for everything!

        • Nicolas Kadis
          24 Sep 2020

          I'm happy to hear that worked out! You're welcome!

  • Chris
    21 Sep 2020

    Thank you very much! Exactly what I needed. I changed the mechanics slightly, because I need to trigger it by click: Instead of .card-container:hover .card-flip { transform: rotateY(180deg); } I use and set .rotated via script .card-flip.rotated { transform: rotateY(180deg); }

    • Nicolas Kadis
      21 Sep 2020

      Hi Chris, I'm happy to hear you found my solution helpful. If you're looking to do the same without any JavaScript, I've got a CodePen with a version where the cards are triggered via click. It uses checkboxes instead of JavaScript. Here's the link: https://codepen.io/nicolaskadis/full/bOLEEd

  • Arjan
    12 Nov 2020

    Did you notice that this effect doesn't work very well on Safari? It's fine on all Chrome, Firefox and Edge (all the decent browsers). Does anyone know how to make Safari play nice as well?

  • Arjan
    12 Nov 2020

    Fix for Safari: add -webkit-backface-visibility to the ".cardflip div" class, like so: .card-flip div { -webkit-backface-visibility: hidden; backface-visibility: hidden; transform-style: preserve-3d; }

    • Gonzalo
      22 Jan 2021

      Hi. I'm facing another issue in Safari on IOS. When fliping the card instead of showing the back side, the fron side mirrors itself by the y axis. Anyone else having this problem?

  • Ludo
    13 Dec 2020

    Bonjour, Merci d'avoir partagé cette solution c'est vraiment super, moi ca marche nickel sur pc mais sur smartphone quand je clique sur l'image elle se retourne, mais elle ne se retourne pas d'elle même et quand je reclique dessus elle ne se retourne pas non plus. Vous avez une astuce peut être?

    • Nicolas Kadis
      13 Dec 2020

      Hi, on mobile phones, when you click away from the card while it's turned on its back, it should turn back to the front side.

  • Thomas
    15 Dec 2020

    Nice one! Thanks. I have prepared a scss version, that will flip the opposite way when the card-container has a hover class: .card-container, .card-flip { transform-style: preserve-3d; transition: all 0.7s ease; } .card-container { perspective: 700px; .back { transform: rotateY(-180deg); } &amp;:hover { .card-flip { transform: rotateY(180deg); } } &amp;.hover { .card-flip { transform: rotateY(180deg); } &amp;:hover { .card-flip { transform: rotateY(0deg); } } } } .card-flip { display: grid; grid-template: 1fr / 1fr; grid-template-areas: "frontAndBack"; transform-style: preserve-3d; transition: all 0.7s ease; &gt; div { backface-visibility: hidden; transform-style: preserve-3d; } .front { grid-area: frontAndBack; } .back { grid-area: frontAndBack; transform: rotateY(-180deg); } }

  • Clement
    16 May 2021

    Thanks Nicolas, this was exactly what I needed. Nice implementation!

  • Aditya Khandelwal
    05 Nov 2021

    I was stuck here. Thank you so much for the help!!!

  • Steeve
    23 Jan 2022

    Thanks a lot! exactly what I was searching for :-)

  • Micke
    26 Oct 2022

    You put me perfectly on the right track to solve it. Works like a charm! Thanks!!

  • Yariv
    25 Dec 2022

    Thanks, precise and clear explanation. I really helped to get a better taste about the grid role. I wonder though ig grid is a viable solution. That is because sub-grid isn't applied on most browsers, and thus the grid inheritance cannot be passed. At my end, this causes problem to align the content. Ex: In my front/back cards I have 3 sub-divs, text, icon, text. I cannot align them to the center. Or better say when I manage to do so, I break the grid margins and height (ex: my-auto; d-flex flex-column, position-absolute, etc'). Any tip towards this direction would be great, grids are an elegant solution. Thanks

    • Nicolas Kadis
      25 Dec 2022

      I haven't tested this out but I'd assume that if you wrap your 3 divs inside another div and define that as a grid that would essentially create the "sub-grid" layout you're looking for. I'd be interested to know if you manage to get it to work.

      • Anonymous
        26 Dec 2022

        I tried it, as it's the second best option... it created all sorts og aligning issues. ex:col don't have the same height; responsiveness didn't work. While I could overcome it with long styling block, and ad-hoc to sm, md, lg screens, (ex define flex-block for sm to make all div fill the width) it isn't clear and clean. At least the best I could come with...

        • avenue17
          05 May 2023

          Now all is clear, thanks for an explanation.

Comment