Flipping a card, CSS animation for web app - css

I've managed to build a card in React with a front and backside and have it flip successfully on hover with CSS. However, I want to flip it on click/touch.
I tried setting the transform property to a class and toggling that class on click but no luck.
How would you accomplish this?
This is how I am currently flipping it via hover:
.card {
position: relative;
perspective: 800px;
transition: transform 1s ease;
perspective: 1000px;
}
.card > * {
width: 10rem;
transition: transform 1s ease;
backface-visibility: hidden;
}
.card:hover > .card__front {
transform: rotateY(180deg);
}
.card:hover > .card__back {
transform: rotateY(0);
}
and my JSX
<div className={styles.card} >
{/* FRONTSIDE */}
<div className={styles.card__front}>
//FRONTSIDE CONTENT
</div>
{/* BACKSIDE */}
<div className={styles.card__back}>
//BACKSIDE CONTENT
</div>
</div>
UPDATE
This function works, it toggles the flip class on and off on my desired element. However, no animation happens once the class is added. The .flip class does the exact same thing as the :hover did though..
//function to toggle class
const flip = (element) => {
console.log(element);
element.classList.toggle("flip");
};
//the css class
.flip {
transform: rotateY(180deg);
}

You may use js to add a style to the element.
What you forgot to add is pretty simple, you forgot transition: transform 1s on the class and perspective: 1000px. The more less the more intense the effect is.
HTML:-
<link href="style.css" rel="stylesheet" type="text/css">
<script src="script.js"></script>
<div class="card-warpper">
<span onclick="flipCard()" class="card">
<div class="front">
Aye
</div>
<div class="back">
Bye
</div>
</span>
</div>
CSS:- (You will edit it based on your needs)
body {
background: blue;
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
.card-warpper {
width: 50%;
height: 30%;
perspective: 1000px;
}
.card {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
transform-style: preserve-3d;
transition: transform 1s;
}
.card-clicked {
transform: rotateY(180deg);
}
.front {
backface-visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
width: 100%;
height: 100%;
background-color: white;
color: black;
font-size: 2rem;
}
.back {
backface-visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
width: 100%;
height: 100%;
background: black;
color: antiquewhite;
font-size: 2rem;
transform: rotateY(180deg);
}
Finally, JS:-
function flipCard() {
let card = document.getElementsByClassName("card")[0];
card.classList.toggle("card-clicked");
}
If this answer helped you, please dont hesitate to click the check mark beside it to accept the answer.

The problem was that React won't rerender the DOM when a class is added with the .toggle method.
The solution was to create a state out of the classlist and add the class from the classlist by using the setState method. This triggers a rerender
//JS
const [classlist, setClasslist] = useState(styles.card)
const flip = () => {
setClasslist(classlist + styles.flip)
}
//HTML
<div className={classlist} onClick={() => flip()}>

I'm still learning javascript but this is what i came up with...
I made a snippet using javascript, you can click the "card" and it goes to the back and when you "mouse out" it rotates back to front or if you comment out the second cards.forEach and you can click another card and it rotates back to front and the card you clicked rotates to the back.
I made a class called ".card__inner.is-flipped". With the "cards" variable declared in the javascript that targets all of the ".card__inner"'s i added and removed the ".is-flipped" class on "click" and "mouseleave" to each card.
let cards = document.querySelectorAll('.card__inner');
cards.forEach(el => {
el.addEventListener('click', function() {
cards.forEach(el => el.classList.remove('is-flipped'));
el.classList.add('is-flipped');
});
});
cards.forEach(el => {
el.addEventListener('mouseleave', function() {
cards.forEach(el => el.classList.remove('is-flipped'));
});
});
.cardRowWrapper { //RowWrapper CARD
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;
flex-wrap: wrap;
width: 45rem;
height: 35rem;
perspective: 800px;
z-index: 100;
}
.card__inner { //Skills circle
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
height: 12rem;
width: 12rem;
border-radius: 50%;
background: green;
transition: transform 1s;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
position: relative;
}
.card__inner.is-flipped {
transform: rotateY(180deg);
background: yellow;
}
.card__face {
position: absolute;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
-o-backface-visibility: hidden;
overflow: hidden;
}
.card__face--front {
display: flex;
backface-visibility: hidden;
align-items: center;
justify-content: center;
}
.card__face--back {
display: block;
transform: rotateY(180deg);
}
<div class="cardRowWrapper">
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
<div class="card__inner">
<div class="card__face card__face--front">
<p>This is the front</p>
</div>
<div class="card__face card__face--back">
<span class="skillsSubHeading">This is the back</span>
</div>
</div>
</div>

Related

CSS: Determine height of flip card in grid automatically without js

I would like to display two flip cards in a row using grid. Each of the flip cards shall contain a squared image on the front and some text on the back. How can I achieve not having to specify any height within the .flip-card css class? I would like the flip card to automatically adjust its height to the width of the image which itself is dependent on the overall space available within the grid (and therefore changes from device to device). At the moment I have to hardcode the height to make it work. But this inevitably leads the flip card not to be squared but to be a rectangle. Any ideas?
This is my code:
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
}
.flip-card {
position: relative;
background-color: transparent;
width: 100%;
height: 200px; /* This value should not be hard coded, but be set according to the width of the image which is determined by the grid. It should make the flip card a perfect square*/
overflow: hidden;
perspective: 1000px;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.flip-card-front {
background-color: transparent;
}
.flip-card-back {
background-color: red;
transform: rotateY(180deg);
}
<div class="grid-container">
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/de/Wikipedia_Logo_1.0.png">
</div>
<div class="flip-card-back">
<p>
Some Text
</p>
</div>
</div>
</div>
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/de/Wikipedia_Logo_1.0.png">
</div>
<div class="flip-card-back">
<p>
Some Text
</p>
</div>
</div>
</div>
</div>
Don't use position:absolute to achieve this. Rely on CSS grid and make both the front and the back of the card on the same area so they overlap while still being in flow element so the area will be defined with the biggest height:
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
}
.flip-card {
overflow: hidden;
perspective: 1000px;
}
.flip-card-inner {
display: grid; /* here */
transition: transform 0.6s;
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
grid-area: 1/1; /*and here */
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.flip-card-back {
background-color: red;
transform: rotateY(180deg);
}
img {
max-width: 100%
}
<div class="grid-container">
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/de/Wikipedia_Logo_1.0.png">
</div>
<div class="flip-card-back">
<p>
Some Text
</p>
</div>
</div>
</div>
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<img src="https://upload.wikimedia.org/wikipedia/commons/d/de/Wikipedia_Logo_1.0.png">
</div>
<div class="flip-card-back">
<p>
Some Text
</p>
</div>
</div>
</div>
</div>

Overflow-x scroll not working on iOS with flex

I already have everything working on the desktop but now I just can't figure out why overflow-x won't work on iOs. I already tried to set the min-width to 0 of all children and also to work it out with table instead of flex.
In the end, it should be a horizontal scroll in the image-container div, that is showing all the images on x-scroll.
As already mentioned, it runs anywhere accept on the iPhone safari browser.
Here is the code so far:
sass:
#main-container {
#image-container {
height: 466px;
width: 95.47vw;
display: flex;
overflow: auto;
-webkit-overflow-scrolling: touch;
#images {
padding-top: 41px;
left: 0;
height: 466px;
width: 1399.8px;
display: flex;
align-items: flex-start;
justify-content: space-between;
border-spacing: 0;
.bodytext {
width: 296.46px;
}
.image {
padding-bottom: 0;
padding-right: 17.5px;
align-self: flex-end;
&:last-child {
padding-right: none;
}
img {
opacity: 100%;
width: 296.46px;
height: auto;
cursor: pointer;
}
img:hover {
opacity: 50%;
}
}
}
}
html:
<div id="main-container">
<div id="image-container">
<div id="images">
<div class="image" id="0-img">
<img ...>
<div class="bodytext">...</div>
<div class="bodytext">...</div>
</div>
<div class="image" id="1-img">
<img ...>
<div class="bodytext">...</div>
<div class="bodytext">...</div>
<div class="image" id="2-img">
<img ...>
<div class="bodytext">...</div>
<div class="bodytext">...</div>
</div>
<div class="image" id="3-img">
<img ...>
<div class="bodytext">...</div>
<div class="bodytext">...</div>
</div>
<img ...>
<div class="bodytext">...</div>
<div class="bodytext">...</div>
</div>
</div>
</div>
Thanks a lot!

css transform rotate flickering/not working

I ran into this issue where I am trying to rotate this div on hover, but when I hover, it tries to hover but then it goes back to the normal position. could you please help?
here is my fiddle: https://jsfiddle.net/Lcb7okfn/
.section-tours{
background-color:pink;
padding:5rem 0 50rem 0;
}
.center-text{
background-color:blue;
padding:30px 0;
}
.col-1-of-3{
width: calc((100%-20px)/3);
}
.card{
background-color: orange;
height:15rem;
transition: all .8s;
perspective: 1000px;
-moz-perspective: 1000px;
}
.card:hover{
transform: rotateY(180deg);
}
Apply the hover to a parent element and also move the perspective declaration to the parent:
.section-tours {
background-color: pink;
padding: 5rem 0 50rem 0;
}
.center-text {
background-color: blue;
padding: 30px 0;
}
h2 {
padding: 0;
margin: 0;
}
.col-1-of-3 {
width: calc((100%-20px)/3);
perspective: 1000px;
}
.card {
background-color: orange;
height: 15rem;
transition: all .8s;
}
.col-1-of-3:hover .card {
transform: rotateY(180deg);
}
<section class="section-tours">
<div class="center-text">
<h2>
Most popular tours
</h2>
</div>
<div class="row">
<div class="col-1-of-3">
<div class="card">
<div class="card class_side">
TEXT
</div>
</div>
</div>
<div class="col-1-of-3">
</div>
<div class="col-1-of-3">
</div>
</div>
</section>

CSS how to break flow in just one direction

Motivation
CSS does not allow styling the options of an html select component, so I'm building a custom "component" to do so.
The challenge is this
Along the x-direction it needs to keep the inline flow.
Along the y-direction, it needs to be able to overlap content below it.
Here's an example (does not break flow)
http://jsfiddle.net/4tyvLmqn/1
html
<div class="dropdown">
<div class="item selected">asdflkhj asdf adf</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">ad fds adfs dfsa fsd fasd</div>
<div class="item">e</div>
</div>
<hr />
This should not move down
<hr />
JS (click handler - there may be a pure CSS way to do this)
document.querySelector('.dropdown .selected').addEventListener('click', e => {
e.target.parentElement.classList.toggle('isOpen')
})
SCSS
.dropdown {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
user-select: none;
border: 1px solid gray;
}
.item {
visibility: hidden;
height: 0;
&.selected {
visibility: visible;
cursor: pointer;
height: auto;
}
.isOpen & {
visibility: visible;
height: auto;
}
}
Simply keep a fixed height then adjust overflow (visible/hidden). You can also consider a max-height in case you will have a lot of elements.
const dropdown = document.querySelector('.dropdown');
dropdown.addEventListener('click', e => {
dropdown.classList.toggle('isOpen')
});
.dropdown {
display: inline-flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
user-select: none;
border: 1px solid gray;
height: calc(1em + 2 * 0.5em);
overflow: hidden;
}
.isOpen {
overflow: visible;
}
.background {
background-color: #fff;
z-index: 1;
border: inherit;
margin: -1px;
max-height: calc(5*(1em + 2 * 0.5em));
}
.isOpen .background {
overflow:auto;
flex-shrink:0;
}
.item {
padding: 0.5em;
cursor: pointer;
}
<div class="dropdown">
<div class="background">
<div class="item">Select an Item</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">ad fds adfs dfsa fsd fasd</div>
<div class="item">e</div>
<div class="item">ad fds adfs dfsa fsd fasd</div>
<div class="item">e</div>
<div class="item">ad fds adfs dfsa fsd fasd</div>
<div class="item">e</div>
</div>
</div>
<hr /> This should not move down
<hr />
Snippet here seems not to be working with CSS3. I have created a JSFiddle for this: http://jsfiddle.net/42mpa6wn/
Javascript:
document.querySelector('.dropdown .selected').addEventListener('click', e => {
e.target.parentElement.parentElement.classList.toggle('isOpen')
})
CSS:
.dropdown {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
user-select: none;
border: 1px solid gray;
background: white;
height:25px;
width:200px;
position:relative;
}
.isOpen .itemlist{
position: absolute;
background: white;
width:inherit;
top:calc(100% - 20px);
}
.item {
visibility: hidden;
height: 0;
&.selected {
visibility: visible;
cursor: pointer;
height: auto;
}
.isOpen & {
visibility: visible;
height: auto;
}
}
HTML:
<hr />
This should not move
<hr />
This should not move
<hr />
<div class="dropdown">
<div class="itemlist">
<div class="item selected">asdflkhj asdf adf</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">ad fds adfs dfsa fsd fasd</div>
<div class="item">e</div>
</div>
</div>
<hr />
This should not move down
<hr />
Basically, you want .dropdown to be relative to the page, where we put it. also, you want .itemlist to be in absolute position when the dropdown action is triggered. You still need to beautify it

Transform makes back side of 3D card unclickable

I want to make flip like http://desandro.github.io/3dtransforms/examples/card-01.html
But with buttons on each side. So here is my html
<div class="flip-container">
<div class="flip">
<div class="side front">
<p>front</p>
<button class="front-flip-button">flip</button>
</div>
<div class="side back">
<p>back</p>
<button class="back-flip-button">flip</button>
</div>
</div>
</div>
and css
.flip-container{
position: relative;
-webkit-perspective: 800;
width: 500px;
height: 300px;
}
.flip{
position: absolute;
width: inherit;
height: inherit;
-webkit-transform-style: preserve-3d;
-webkit-transition-duration: 500ms;
-webkit-transform: translate3d(0, 0, 0);
border: 1px solid;
}
.side{
position: absolute;
width: inherit;
height: inherit;
-webkit-transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
}
.flip.flipped,
.back{
-webkit-transform: rotate3d(0, 1, 0, 180deg);
}
.back-flip-button,
.front-flip-button{
position: absolute;
left: 0;
top: 0;
}
p{
font-size: 90px;
text-align: center;
}
And everything is pretty good, but when I use some element with translateZ it makes back button unclickable.
<div class="flip-container">
<div class="flip">
<div class="side front">
<p>front</p>
<button class="front-flip-button">flip</button>
</div>
<div class="side back">
<p>back</p>
<button class="back-flip-button">flip</button>
</div>
</div>
</div>
<div class="some"></div>
Why is that happening?
http://jsfiddle.net/j5Sm4/
Apply this
.back-flip-button {
... Your other styles ...
-webkit-transform:translateZ(1px);
-webkit-backface-visibility:hidden;
/* Needs other prefixes to be cross browser */
}
Demo
This is because the parent element gets its own stacking order. To equalize the stacking order and thus put the button on top, you have to give it a stacking order which I did using translateZ. The backface-visibility:hidden makes the button not show on the front side
In the future we will likely be able to do this by applying the will-change property to the back side

Resources