Is the CSS property `backface-visibility` causing compatibility issues across different browsers with animations?

I have created a unique flip animation that displays new numbers resembling an analog clock or calendar with a hinge in the middle.

The process is simple: using a div, I have:

  • The bottom half of the first number on one side
  • The top half of the second number rotated 180 degrees to show on the back

To reveal the new number, I rotate the entire div around the center of the container, displaying the back of the rotating div: View Number flip animation in the latest Firefox

However, in Chrome, the animation does not always work as expected. Sometimes half of the content disappears until the animation is complete, and sometimes the old number doesn't render properly: Number flip animation in the latest Chrome with the bottom of the number not appearing until after the animation is complete

In Safari 12, the situation worsens as it seems to disregard the backface-visibility, even with the -webkit- prefix: Safari 12 Number animation, the bottom half of the first number is inverted after the animation is complete

While the pre-Chromium Edge browser handles this issue well, the new Edge version (v83) experiences similar problems to Chrome.

I have tried adjusting the properties and have explored other questions related to backface-visibility on this platform.

Here is the code; hover over the numbers to witness the flip:

body {
  background: #2e517d;
}

.container {
  width: 175px;
  height: 192px;
  background: #4e9bfa;
  position: relative;
  left: 50%;
  transform: translate(-50%, 50%);
  perspective: 1000px;
}

.cover {
  width: 175px;
  height: 50%;
  position: absolute;
  top: 96px;
  background-color: #34b58c;
  transform: rotateX(0deg);
  transform-style: preserve-3d;
  transform-origin: top;
  transition: all 0.5s ease-out;
}

.container:hover .cover {
  transform: rotateX(180deg);
}

.flip {
  margin: 0;
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
}

.container p {
  font-size: 1000%;
  margin: 0;
}

.container>p {
  height: 96px;
  overflow: hidden;
}

.front-number-bottom {
  position: relative;
  height: 96px;
  overflow: hidden;
  background-color: red;
}

.front-number-bottom p {
  margin: 0;
  position: relative;
  top: -96px;
}

.back-number-top {
  position: relative;
  height: 96px;
  overflow: hidden;
}

.back-number-bottom {
  height: 96px;
  overflow: hidden;
  position: relative;
  z-index: -1;
}

.back-number-bottom p {
  margin: 0;
  position: relative;
  top: -96px;
}

div.front {
  background: red;
}

div.back {
  background: green;
  transform: rotateX(180deg);
}
<body>
  <div class="container>
    <p>76</p>
    <div id="cover" class="cover">
      <div class="flip front">
        <div class="front-number-bottom">
          <p>76</p>
        </div>
      </div>
      <div class="flip back">
        <div class="back-number-top">
          <p>77</p>
        </div>
      </div>
    </div>
    <div class="back-number-bottom">
      <p>77</p>
    </div>
  </div>
  </div>
</body>

Is this a viable approach that can be easily rectified in Chromium browsers and Safari?

Would an alternate approach be more effective?

Answer №1

Your code seems a bit intricate to me. I would suggest simplifying your logic as shown below, eliminating the need for backface-visibility: hidden;

Take note of two important elements:

  • The mask that allows me to slice the element and display only 50% of the height (either top or bottom). This will enhance the realism of the animation since each number will have distinct top and bottom parts.
  • The z-index technique where I implement a transition that alters the z-index precisely at the midpoint of the animation (when the rotations reach 90deg)1.

.card {
  width: 175px;
  height: 192px;
  position: relative;
  z-index:0;
  left: 50%;
  transform: translate(-50%, 50%);
  font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  position:absolute;
  z-index:2;
  perspective: 1000px;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:attr(data-number);
  -webkit-mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
          mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  -webkit-mask-position:bottom;
          mask-position:bottom;
  background:green;
}

.card span:first-child::after {
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}

/* Hover */
.card:hover span:first-child {
  z-index:1;
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:last-child::before {
    transform: rotateX(0deg);
}
<div class="card">
  <span data-number="76"></span>
  <span data-number="77"></span>
</div>

The mask can also be replaced with clip-path for optimization:

.card {
  width: 175px;
  height: 192px;
  position: relative;
  z-index:0;
  left: 50%;
  transform: translate(-50%, 50%);
  font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  z-index:2;
  perspective: 1000px;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:attr(data-number);
  clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
  background:green;
}

.card span:first-child::after {
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}

/* Hover */
.card:hover span:first-child {
  z-index:1;
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:last-child::before {
    transform: rotateX(0deg);
}
<div class="card">
  <span data-number="76"></span>
  <span data-number="77"></span>
</div>


Another optimization involves using counter without specifying explicit width/height:

.card {
  margin:0 5px;
  font-family:monospace;
  display:inline-block;
  text-align:center;
  position: relative;
  z-index:0;
  font-size: 150px;
  counter-reset:num calc(var(--n,1) - 1);
}
/* this will defined the height/width*/
.card::after {
  content:counter(num);
  visibility:hidden;
}
/**/
.card span,
.card span::before,
.card span::after {
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
}
.card span {
  z-index:2;
  perspective: 1000px;
  counter-increment:num;
}
.card span:first-child {
  z-index:3;
  transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
  content:counter(num);
  clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
  background:red;
  transition:0.5s all linear;
  transform-style: preserve-3d;
}
.card span::after {
  clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
  background:green;
}

.card span:first-child::after,
.card:hover span:last-child::before{
    transform: rotateX(0deg);
}
.card span:last-child::before {
    transform: rotateX(-180deg);
}
.card:hover span:first-child::after {
    transform: rotateX(180deg);
}
.card:hover span:first-child {
  z-index:1;
}
<div class="card" style="--n:75">
  <span></span><span></span>
</div>

<div class="card" style="--n:5">
  <span></span><span></span>
</div>

<div class="card" style="--n:100">
  <span></span><span></span>
</div>


1 Using linear is straightforward, but it can be trickier with other ease functions. Here's a related question that might help in identifying the midpoint of ease functions: When exactly does an ease animation reach its midpoint?

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ensure the first column is fixed and all other columns have the same height

I have encountered an issue where the first column of my table has varying heights compared to the other columns, resulting in inconsistent row heights. Below is the code I am using: <div class="outer"> <div class="inner"> <table> ...

Accordion-inspired multi-layer list navigation

I have been searching everywhere but haven't found a solution to this issue. My list menu has the following markup: <div id="menu"> <ul> <li>Category #1 <ul> <li>Item #1</li> <li>I ...

By default, the first table row is displayed when using table grouping in AngularJS

I am attempting to display only the first tr in the table and hide all other tr elements by default. I have tried using ng-show and ng-hide but it does not seem to be working as expected. This is not originally my plunker; I used it for reference on group ...

Dividing the screen into multiple sections to display cards

I have a question about integrating product cards into a split screen layout. Despite my efforts, the cards are stacking on top of each other instead of appearing side-by-side. Does anyone have a solution or suggestion? Below is the code I have been work ...

Combining an image and a link in HTML CSS: Tips for stacking elements on top of each other

I am currently working on adding a hyperlink to an image, so that when the image is clicked it will navigate to the specified link. However, I am having trouble with the formatting of the hyperlink as it is appearing smaller than the actual image. Below ...

Transform your image using the Bootstrap framework

I am attempting to create a similar banner using bootstrap. You can view the demo of the banner here. https://i.stack.imgur.com/JXLNY.png I'm struggling to understand how to achieve this in bootstrap 3 and where to begin. Should I use rows and colum ...

Creating spacious gaps between list items

I have been working on creating a scrollable horizontal list using iscroll. Although the list functions properly, I am encountering an issue with spacing between each li item. Despite trying various solutions, I have been unable to remove the space betwe ...

Issue with Bootstrap 5 Navbar menu link functionality after implementing 'data-bs-toggle' and 'data-bs-target' properties

I found a solution to the issue with the responsive menu not working as expected after following a post: Bootstrap close responsive menu "on click" When I click on the menu link, it fails to move to the intended section on the page. <link rel="styl ...

Clean coding techniques for toggling the visibility of elements in AngularJS

Hey everyone, I'm struggling with a common issue in my Angular projects: app.controller('indexController', function ($scope) { scope.hideWinkelContainer = true; scope.hideWinkelPaneel = true; scope.headerCart = false; scope. ...

Emphasize entries in an index that match the currently visible content as you scroll

I have a table of contents in my HTML page with the CSS attribute position: fixed;. I want to emphasize the current reading position by highlighting it (bold or italics) as I scroll down the page. | yada yada yada ... 1. Secti ...

The alignment of the timeline in HTML CSS becomes distorted once it reaches the fourth element

As a newcomer to HTML, CSS, and JS, I am currently embarking on the task of creating a personal website for a course project. One of the pages on my website showcases a timeline featuring dates and information. While I have referred to a tutorial on W3scho ...

Changing colors when hovering over different elements

I am struggling to create a hover color change effect that applies to all elements (icon, text, and button) from top to bottom. Currently, the hover effect only changes the color of either the text and icon or the button individually. Below is the code sn ...

Emphasize a checkbox by selecting it when another checkbox is checked

I have a question about checkboxes and highlighting the checkmarks. The issue I am facing is that I have multiple checkboxes with the same ID for different screen resolutions. When I click on the label for "Check 1" it highlights the corresponding checkmar ...

Create a dynamic webpage that accepts user input, processes it using R code, and then displays the resulting output back on the webpage

I am in the process of creating a front end interface to showcase the results of my R code, which requires two files: master.csv and detail.csv. The master.csv file contains reference data with two columns, Commodity Category and Commodity Name, listed as ...

Guide on making a color legend with JavaScript hover effects

On my website, there is a dynamic element that displays a table when values are present and hides it when there are no values. The values in the table are color-coded to represent different definitions. I wanted to add hover text with a color key for these ...

Identifying HTML paragraph tags in VB.NET within a web page

Currently developing an application that allows logging into your Mojang account and fetching details. Everything is functioning smoothly, however, there is an issue where a message is displayed on the page if the wrong account username or password is ente ...

Require assistance resolving a CSS problem on IE8?

Greetings everyone, I am currently facing some compatibility issues with my style sheets, particularly in IE 8. The CSS code below functions perfectly in my Chrome/Firefox style sheet, but unfortunately, it does not have any effect on my IE stylesheet. # ...

Preventing Content Jumping When Concealing Elements: Tips and Tricks

I've been working on a project at , where you can click on a small map to expand a larger map below it. However, I noticed that when you scroll down to the large map and then try to close it using the button at the bottom, the content on the page jum ...

socket.io, along with their supporting servers

Can socket.io function separately from being the webserver? I prefer to utilize an external webserver, but in order for it to function properly I require /socket.io/socket.io.js. Is there a method other than duplicating[1] this file? I want to avoid comp ...

Close the ionicPopup by tapping anywhere

Currently, I have implemented an ionicPopup that automatically closes after a certain time limit. However, I am wondering if there is a way to configure it to close with a single or double tap anywhere on the screen instead. While I am aware that setting a ...