Search code examples
javascriptreactjswebpacksasscreate-react-app

CSS keyframes working fine in dev environment but not on production build


I am working on an app that uses node-sass to compile SCSS. My component structure is like this:

./src
|-- components
|   |-- Foo
|   |   |-- Foo.jsx
|   |   `-- Foo.scss
|   |-- Bar
|       |-- Bar.jsx
|       `-- Bar.scss
[...]

An then I'm calling my scss like so:

import React, {useState} from 'react';
import './Foo.scss';
[...]

When I'm running the dev build, all my transitions are ok, but when I run build and deploy, they are lost. All other CSS styles work fine, it's just my transitions get screwed. They're still there, but they all happen in the last frame. So if a transition is supposed to go from 0 to 100 in 200ms, it still happens, but it goes from 0 to 100 in the last possible frame.

Can anyone point me in the right direction? Here's my package.json:

{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "node-sass": "^4.14.1",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-icons": "^3.10.0",
    "react-map-gl": "^5.2.7",
    "react-map-gl-geocoder": "^2.0.12",
    "react-redux": "^7.2.0",
    "react-router": "^5.2.0",
    "react-router-dom": "^5.2.0",
    "react-scripts": "^3.4.3",
    "redux": "^4.0.5",
    "redux-thunk": "^2.3.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

EDIT: As per requested, here's a sample of my transitions:


.fade-in {
    animation: fade-in ease-in 0.25s,
    slide-in ease-in 0.25s;
}

@keyframes fade-in {
    0% {
        opacity: 0%;
    }
    
    100% {
        opacity: 100%;
    }
}

@keyframes slide-in {
    0%{
        transform: translateY(50px);
    }
    100%{
        transform: translateY(0px);
    }
}

Then I'm adding the classes conditionally depending on the state.


Solution

  • Short Answer: .scss doesn't support % notation (neither is it the official notation according to the w3 documentation: https://www.w3.org/TR/css-color-3/#opacity) Some browsers don't support % notation either. Change your opacity values from % notation to number notation between 0 and 1.

    Long Answer: I had a similar issue. In a create-react-app environment, the following animated correctly when I ran it with npm start on the cli, but didn't work when I ran npm run build and serve -s build. My opacity 0 elements would suddenly hit its end opacity on the last frame (like yours.) Here's my original custom .css file:

    /*keyframes.css*/
    @keyframes slideInRightDark {
      0% {
        opacity: 0%;
        transform: translate(100%);
      }
      100% {
        opacity: 40%;
        transform: translate(0%);
      }
    

    And the react component file:

    /*Text.js*/
    import "[...some_file_path]/keyframes.css"
    ...
    return (<div style={{animationName: slideInRightDark, animationDuration... etc.}}></div>)
    

    I got it to work by looking at the animate.css library (https://animate.style/) and observing how the animate.css keyframes were built. BTW, great library for instantly usable keyframes. Highly recommended for quick-starting your web apps. Anyway, here's from the animate.css file:

    /* CSS keyframes from animate.css */
    @charset "UTF-8";
    :root {
      --animate-duration: 1s;
      --animate-delay: 1s;
      --animate-repeat: 1;
    }
    ...
    @-webkit-keyframes slideInLeft {
      from {
        -webkit-transform: translate3d(-100%, 0, 0);
        transform: translate3d(-100%, 0, 0);
        visibility: visible;
      }
    
      to {
        -webkit-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
      }
    }
    @keyframes slideInLeft {
      from {
        -webkit-transform: translate3d(-100%, 0, 0);
        transform: translate3d(-100%, 0, 0);
        visibility: visible;
      }
    
      to {
        -webkit-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
      }
    }
    

    After swapping in & out the utf-charset and :root portion as well as adding my own @-webkit-keyframes, I found that it was actually the opacity being in percentage notation that made it blink on suddenly after its animation-duration:

    /*in keyframes.css*/
    ...
    @keyframes slideInRightDark {
      from {
        opacity: 0;
        transform: translate(100%, 0);
      }
      to {
        opacity: 0.4;
        transform: translate(0, 0);
      }
    }
    

    I hope that helps