htmlcssr-markdownflexdashboard

Avoid Flickering (Multiple Rotations) for an Element Rotated by CSS


Background

I am not a web developper, but a Data Scientist, who builds a dashboard using R Markdown with library(flexdashboard). That is, I rely on a given css framework, which I want to extend using some home-brew CSS/HTML.

Goal

I want to create a visual which mimics flexdashboard's own .value-box, but adds a hover effect, which make the value box rotate and show a different value box.

My attempt

I managed to create a rotating value box of sorts, i.e. it does the rotating and looks pretty much as the out-of-the-box value box. But the rotating is not very clean, i.e. it seems as several attempts to start the rotating happen at once.

Expected Solution

I like that the flipbox upon hover rotates cleanly as long as the mouse is over the element. As soon as it leaves ot should rotate back. In the attached gif, you can see some attempts where there is only one clean rotation.

Example Gif of the Unclean Solution

Gif showing the stuttering / flickering effect of the flipbox

My Research

I have found an answer here: CSS Rotating Info Cards are Flickering and I think the answer could solve my problem, but I am not sure how to adopt it. How would I need to change the markup the css rules to get a clean flipping effect?

Code

(I am aware that it is not 100% minimal, but tries to mimic at least some of the look and feel of the original valueBox)

/* Simplified Framework (rmarkdown flexdashboard) CSS */
.page {
  display: flex;
  flex-direction: column;
}

.wrapper {
  display: flex;
  flex-direction: column;
}

.row {
  display: flex;
  flex-direction: row;
}

.value-box {
    border-radius: 2px;
    position: relative;
    display: block;
    margin-right: 8px;
    margin-bottom: 8px;
    color: #fff;
}

.value-box > .inner {
    padding: 10px;
    padding-left: 20px;
    padding-right: 20px;
}

.value-box .value {
    font-size: 38px;
    font-weight: bold;
    margin: 0 0 3px 0;
    white-space: nowrap;
    padding: 0;
}

.value-box .caption {
    font-size: 15px;
}

.fa {
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    display: inline-block;
    position: absolute;
    top: 15px;
    right: 15px;
    font-size: 80px;
    color: rgba(0, 0, 0, 0.15);      
}

/* Custom Flipbox */

.value-box-parent {
  position: relative;
  transform-style: preserve-3d;
}

.value-box.front, .value-box.back {
  transition: transform 0.5s ease;
  position: absolute;
  width: 100%;
  -webkit-backface-visibility: hidden;
}

.back {
  transform: perspective(1000px) rotateY(180deg);
}

.value-box-parent:hover .back {
  transform: perspective(1000px) rotateY(0deg);
}

.front {
  transform: perspective(1000px) rotateY(0deg);
}

.value-box-parent:hover .front {
  transform: perspective(1000px) rotateY(-180deg);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet"/>
<div class="page" style="height: 100%;">
  <div class="wrapper" style="height: 100%;">
    <div class="row" style="flex: 150 150 0px;">
      <!-- Out of the Box Value Box -->
      <div class="value-box" style="background-color: rgb(31, 158, 137); flex: 576 576 0px;">
        <div class="inner">
          <p class="value">123</p>
          <p class="caption">Number</p>
        </div>
        <div class="icon"><i class="fa fa-hashtag"></i></div>
      </div>
      <!-- My Attempt of a FlipBox -->
      <div class="value-box" style="flex: 576 576 0px;">
        <!-- The following markup can be freely changed -->
        <div class="value-box-parent">
          <div class="value-box front" style="background-color: rgb(38, 130, 142);">
            <div class="inner">
              <p class="value">1</p>
              <p class="caption">FlipBox</p>
            </div>
            <div class="icon"><i class="fa fa-cog"></i></div>
          </div>
          <div class="value-box back" style="background-color: rgb(31, 158, 137);">
            <div class="inner">
              <p class="value">2</p>
              <p class="caption">FlipBox</p>
            </div>
            <div class="icon"><i class="fa fa-cog"></i></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>


Solution

  • What causes the flickering:

    1. .front and .back have position: absolute;
    2. This causes them to be taken out of the flow and make their parent (.value-box-parent) collapse to 0 height
    3. Even when .value-box-parent has 0 height, it can still receive pointer events passed from its children (.front and .back)
    4. But during the flipping, if the pointer still moving, it may fall out of their areas.
    5. This result in .value-box-parent flip flop between :hover and normal state.

    How to fix:

    /* Simplified Framework (rmarkdown flexdashboard) CSS */
    
    .page {
      display: flex;
      flex-direction: column;
    }
    
    .wrapper {
      display: flex;
      flex-direction: column;
    }
    
    .row {
      display: flex;
      flex-direction: row;
    }
    
    .value-box {
      border-radius: 2px;
      position: relative;
      display: block;
      margin-right: 8px;
      margin-bottom: 8px;
      color: #fff;
    }
    
    .value-box>.inner {
      padding: 10px;
      padding-left: 20px;
      padding-right: 20px;
    }
    
    .value-box .value {
      font-size: 38px;
      font-weight: bold;
      margin: 0 0 3px 0;
      white-space: nowrap;
      padding: 0;
    }
    
    .value-box .caption {
      font-size: 15px;
    }
    
    .fa {
      font-family: "Font Awesome 6 Free";
      font-weight: 900;
      display: inline-block;
      position: absolute;
      top: 15px;
      right: 15px;
      font-size: 80px;
      color: rgba(0, 0, 0, 0.15);
    }
    
    
    /* Custom Flipbox */
    
    .value-box-parent {
      position: absolute;
      inset: 0;
      transform-style: preserve-3d;
    }
    
    .value-box.front,
    .value-box.back {
      transition: transform 0.5s ease;
      position: absolute;
      width: 100%;
      -webkit-backface-visibility: hidden;
    }
    
    .back {
      transform: perspective(1000px) rotateY(180deg);
    }
    
    .value-box-parent:hover .back {
      transform: perspective(1000px) rotateY(0deg);
    }
    
    .front {
      transform: perspective(1000px) rotateY(0deg);
    }
    
    .value-box-parent:hover .front {
      transform: perspective(1000px) rotateY(-180deg);
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet" />
    <div class="page" style="height: 100%;">
      <div class="wrapper" style="height: 100%;">
        <div class="row" style="flex: 150 150 0px;">
          <!-- Out of the Box Value Box -->
          <div class="value-box" style="background-color: rgb(31, 158, 137); flex: 576 576 0px;">
            <div class="inner">
              <p class="value">123</p>
              <p class="caption">Number</p>
            </div>
            <div class="icon"><i class="fa fa-hashtag"></i></div>
          </div>
          <!-- My Attempt of a FlipBox -->
          <div class="value-box" style="flex: 576 576 0px;">
            <!-- The following markup can be freely changed -->
            <div class="value-box-parent">
              <div class="value-box front" style="background-color: rgb(38, 130, 142);">
                <div class="inner">
                  <p class="value">1</p>
                  <p class="caption">FlipBox</p>
                </div>
                <div class="icon"><i class="fa fa-cog"></i></div>
              </div>
              <div class="value-box back" style="background-color: rgb(31, 158, 137);">
                <div class="inner">
                  <p class="value">2</p>
                  <p class="caption">FlipBox</p>
                </div>
                <div class="icon"><i class="fa fa-cog"></i></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>