Search code examples
javascriptcssreactjsstyled-components

How to use @property in styled components?


I want to use animation in my app, but it requires @property function from SCSS:

@property --border-angle {
  syntax: "<angle>";
  inherits: true;
  initial-value: 0turn;
}

Is there a way to do it in styled-components?

The whole code for animation is on: https://codepen.io/shshaw/pen/RwJwJJx

Or how to re-write this function so it does not have to use property function?


Solution

  • The posted code does seem to work with styled-components as I tested, although it seems that the browser support for @property is still limited, such as it works for Chrome but not currently for Firefox, therefore the gradient animation will not play on it.

    I tried to create an alternative version of the posted code without the use of @property, which runs on Firefox as well. In case if it could be useful, here is a demo on: stackblitz (code included at the end of the answer).

    The original posted code was tested with below example on: stackblitz (gradient animation by @property not currently supported by Firefox).

    // Styled components
    const Container = styled.div`
      height: 100%;
      background: #223;
      display: grid;
      place-items: center;
    `;
    
    const Box = styled.div`
      --border-size: 3px;
      --border-angle: 0turn;
      width: 60vmin;
      height: 50vmin;
      background-image: conic-gradient(
          from var(--border-angle),
          #213,
          #112 50%,
          #213
        ),
        conic-gradient(from var(--border-angle), transparent 20%, #08f, #f03);
      background-size: calc(100% - (var(--border-size) * 2))
          calc(100% - (var(--border-size) * 2)),
        cover;
      background-position: center center;
      background-repeat: no-repeat;
      animation: bg-spin 3s linear infinite;
      @keyframes bg-spin {
        to {
          --border-angle: 1turn;
        }
      }
      &:hover {
        animation-play-state: paused;
      }
      @property --border-angle {
        syntax: "<angle>";
        inherits: true;
        initial-value: 0turn;
      }
    `;
    
    export default function App() {
      return (
        <Container>
          <Box></Box>
        </Container>
      );
    }
    

    Below is the alternative version without @property for comparison, it used pseudo-element and added a child div to recreate the animation in styled-components.

    Live demo on: stackblitz (should also work for Firefox).

    // Styled components
    const Container = styled.div`
      min-height: 100vh;
      background: #223;
      display: grid;
      place-items: center;
    `;
    
    const Box = styled.div`
      width: 60vmin;
      height: 50vmin;
      position: relative;
      overflow: hidden;
      &::before {
        content: "";
        position: absolute;
        inset: 0;
        background-image: conic-gradient(from 0turn, transparent 20%, #08f, #f03);
        animation: fallback-spin 3s linear infinite;
      }
      @keyframes fallback-spin {
        to {
          transform: scale(1000%) rotate(1turn);
        }
      }
      &:hover::before {
        animation-play-state: paused;
      }
      &:hover > div::before {
        animation-play-state: paused;
      }
    `;
    
    const Fallback = styled.div`
      position: absolute;
      inset: 3px;
      overflow: hidden;
      background-color: pink;
      &::before {
        content: "";
        position: absolute;
        inset: 0;
        background-image: conic-gradient(from 0turn, #213, #112 50%, #213);
        animation: fallback-spin 3s linear infinite;
      }
      @keyframes fallback-spin {
        to {
          transform: scale(1000%) rotate(1turn);
        }
      }
    `;
    
    export default function App() {
      return (
        <Container>
          <Box>
            <Fallback></Fallback>
          </Box>
        </Container>
      );
    }
    

    @property is newer but standard CSS, by the way. More background about @property on MDN.