Filp card back when other card is clicked using CSS

Viewed 277

I'm trying to create a flip card functionality that works on hover in desktop view while on click in mobile view. I'm able to achieve both using checkbox and entirely using CSS.

Here's the code snippet:

.flip-card {
    width: 150px;
    height: 180px;
    margin: 1em;
    perspective: 1500px;
}
.flip-card .flip-card-inner {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 0.8s cubic-bezier(0.75, 0, 0.85, 1);
}
.more {
    display: none;
}
.more:checked ~ .flip-card-inner {
    transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
    transform-style: preserve-3d;
    border-radius: 6px;
}
.inner {
    height: 100%;
    display: grid;
    padding: 1.5em;
}
.flip-card-front {
    background-color: #2980b9;
}

.flip-card-back {
    transform: rotateY(180deg);
    background-color: #fff;
    border: 2px solid #f0f0f0;
}

@media screen and (min-width: 900px){
.flip-card-inner:hover {
    transform: rotateY(180deg);
  }
}
<div class="flip-card col-6">
    <input type="checkbox" id="card1" class="more" aria-hidden="true">
    <div class="flip-card-inner">
      <label for="card1"  aria-hidden="true">
      <div class="flip-card-front" style="background-color: rgba(96,132,38,0.4); box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">

      
        <div class="font-weight-bold flip-card-front-book-title ml-2 mb-2">Outliers</div>

        <div class="font-weight-bold flip-card-front-book-author ml-2" style="color: #A2671A;"> <span
            class="text-muted">By</span> Malcolm Gladwell</div>
      
      </div>
    </label>
    <label for="card1"  aria-hidden="true">
      <div class="flip-card-back" style="box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">
    
        <div>
          <h5 >Added by</h5>
          <strong >Jitesh</strong>
        </div>
      </div>
    </label>
    </div>
</div>

<div class="flip-card col-6 ">
    <input type="checkbox" id="card2" class="more" aria-hidden="true">
    <div class="flip-card-inner">
      <label for="card2"  aria-hidden="true">
      <div class="flip-card-front" style="background-color: rgba(96,132,38,0.4); box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">

      
        <div class="font-weight-bold flip-card-front-book-title ml-2 mb-2">Shoe Dog</div>

        <div class="font-weight-bold flip-card-front-book-author ml-2" style="color: #A2671A;"> <span
            class="text-muted">By</span> Phil Knight</div>
      
      </div>
    </label>
    <label for="card2"  aria-hidden="true">
      <div class="flip-card-back" style="box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">
    
        <div>
          <h5 >Added by</h5>
          <strong >Ishant</strong>
        </div>
      </div>
    </label>
    </div>
</div>

I am displaying multiple cards and using their unique IDs for flip event on mobile view. Now, I need to add a functionality to flip the card back once any other card is clicked or I click anywhere else. (Only for mobile view). Currently I have to manually click each card to flip it back to orignal.

I'm looking for CSS based solution if possible.

P.S. If it's not possible using pure CSS, please provide a working solution using jquery as I'm new to it.

1 Answers

Here you have a CSS-only solution (with a catch that I'll explain below):

.flip-card {
  width: 150px;
  height: 180px;
  margin: 1em;
  perspective: 1500px;
  display: inline-block;
}

#no-card-open:not(:checked) ~ .flip-card {
  position: relative;
  z-index: 2;
}

.flip-card .flip-card-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s cubic-bezier(0.75, 0, 0.85, 1);
}

input[name="card-control"] {
  display: none;
}

.more:checked + .flip-card-inner {
  transform: rotateY(180deg);
}

.flip-card-front,
.flip-card-back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  transform-style: preserve-3d;
  border-radius: 6px;
}

.inner {
  height: 100%;
  display: grid;
  padding: 1.5em;
}

.flip-card-front {
  background-color: #2980b9;
}

.flip-card-back {
  transform: rotateY(180deg);
  background-color: #fff;
  border: 2px solid #f0f0f0;
}

@media screen and (min-width: 900px) {
  .flip-card-inner:hover {
    transform: rotateY(180deg);
  }
}

#screen {
  display: fixed;
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1;
}

#no-card-open:checked ~ #screen {
  display: none;
}
<input type="radio" id="no-card-open" name="card-control" checked />
<label id="screen" for="no-card-open"></label>

<div class="flip-card col-6">
  <input type="radio" id="card1-open" name="card-control" class="more" />
  <div class="flip-card-inner">
    <label for="card1-open" aria-hidden="true">
      <div class="flip-card-front" style="background-color: rgba(96,132,38,0.4); box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">      
        <div class="font-weight-bold flip-card-front-book-title ml-2 mb-2">Outliers</div>
        <div class="font-weight-bold flip-card-front-book-author ml-2" style="color: #A2671A;"> <span
            class="text-muted">By</span> Malcolm Gladwell</div>
      </div>
    </label>
    <label for="no-card-open" aria-hidden="true">
      <div class="flip-card-back" style="box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">
    
        <div>
          <h5 >Added by</h5>
          <strong >Jitesh</strong>
        </div>
      </div>
    </label>
  </div>
</div>

<div class="flip-card col-6 ">
  <input type="radio" id="card2-open" name="card-control" class="more" />
  <div class="flip-card-inner">
    <label for="card2-open" aria-hidden="true">
      <div class="flip-card-front" style="background-color: rgba(96,132,38,0.4); box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">

      
        <div class="font-weight-bold flip-card-front-book-title ml-2 mb-2">Shoe Dog</div>

        <div class="font-weight-bold flip-card-front-book-author ml-2" style="color: #A2671A;"> <span
            class="text-muted">By</span> Phil Knight</div>
      
      </div>
    </label>
    <label for="no-card-open" aria-hidden="true">
      <div class="flip-card-back" style="box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">
    
        <div>
          <h5 >Added by</h5>
          <strong >Ishant</strong>
        </div>
      </div>
    </label>
  </div>
</div>


<div class="flip-card col-6 ">
  <input type="radio" id="card3-open" name="card-control" class="more" />
  <div class="flip-card-inner">
    <label for="card3-open" aria-hidden="true">
      <div class="flip-card-front" style="background-color: rgba(96,132,38,0.4); box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">

      
        <div class="font-weight-bold flip-card-front-book-title ml-2 mb-2">Incognito</div>

        <div class="font-weight-bold flip-card-front-book-author ml-2" style="color: #A2671A;"> <span
            class="text-muted">By</span> David Eagleman</div>
      
      </div>
    </label>
    <label for="no-card-open" aria-hidden="true">
      <div class="flip-card-back" style="box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.4),
    -3px -3px 4px rgba(255, 255, 255, 1);">
    
        <div>
          <h5 >Added by</h5>
          <strong >Alvaro</strong>
        </div>
      </div>
    </label>
  </div>
</div>

The idea behind this solution: use radio buttons instead of checkboxes.

A group of radio buttons with the same name can only have a maximum of one active radio at a time. And you only want to have one book/card flipped at a time. Based on that, radio buttons seems like a more natural solution than checkboxes.

So, step 1: replace the checkboxes with radio buttons.

Now we find the first problem: this way, only one card can be flipped at a time. But if I click on the flipped card, it should go to its original position... and right now, it doesn't. You can solve this by adding a new radio button that will indicate that no card is flipped.

The label of the card in a normal state will point to radio for that card. Instead of pointing to the card radio, a flipped card's label will point to the "no card is flipped" radio.

Step 2: add a radio to handle the no-card-flipped state.

Still, there's one last problem. You need to click on a card to flip it back to normal. When you click outside, nothing happens, and the flipped card remains flipped even when you want it to go to the normal state.

To avoid that, you can create a visually hidden label that will point to the no-card-flipped state. This new label will cover the whole viewport and go on top of everything but the cards (in the example, I did z-index:1 for the screen label and z-index:2 for the cards). The screen label will only be visible when a card is flipped. That way, wherever you click on the screen (except on the cards), it will return the flipped card to its normal position and hide the screen.

Step 3: add a "hidden" screen to return all cards to the normal position.

And here is where the catch is: because the screen covers the whole viewport while a card is open, you may see some weird behaviors like, for example, you click on a link or button, and nothing happens the first time, you have to click it twice. This is because the first click is to hide the screen.

And that's the CSS-only solution with a catch :)


Note: while there may be a CSS option, I would not recommend going with it. The solution above is specific to the example given in the question, and it may fail in a more complex environment.

Also, the complexity of the whole system grows considerably. The more cards there are, the more radio buttons there will be. It would be easier and faster to manage the states with JavaScript (plus it is probably better accessibility wise too).

Related