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:
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:
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:
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.
Date T
Thanks for sharing, just what I wanted!
Lili
Thank you ! Very useful.
DJ
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&uc=engineer-to-order. It's the leanest solution I've found for this, just stuck on this last little thing...Thanks!
Nicolas Kadis
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
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
I'm happy to hear that worked out! You're welcome!
Chris
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
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
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
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
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
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
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
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); } &:hover { .card-flip { transform: rotateY(180deg); } } &.hover { .card-flip { transform: rotateY(180deg); } &: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; > div { backface-visibility: hidden; transform-style: preserve-3d; } .front { grid-area: frontAndBack; } .back { grid-area: frontAndBack; transform: rotateY(-180deg); } }
Clement
Thanks Nicolas, this was exactly what I needed. Nice implementation!
Aditya Khandelwal
I was stuck here. Thank you so much for the help!!!
Steeve
Thanks a lot! exactly what I was searching for :-)
Micke
You put me perfectly on the right track to solve it. Works like a charm! Thanks!!
Yariv
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
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
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
Now all is clear, thanks for an explanation.