Reversing animation in styled components when a prop is set to false

One interesting challenge I encountered recently was passing an open prop to a styled component in order to implement animations for a hamburger icon. Here is the code I used:

const StyledBurger = styled.button`
  display: flex;
  flex-direction: column;
  justify-content: center;
  border: 0;
  background-color: ${colors.cobaltBlue};
  border-radius: 2.7px;
  cursor: pointer;
  div {
    width: 27px;
    height: 3px;
    margin: 1.5px;
    transition: all 0.2s linear;
    border-radius: 1.4px;
    background-color: ${colors.white};
    :first-child {
      ${({ open }) => open && firstOpenAnimation};
    }
    :nth-child(2) {
      opacity: ${({ open }) => (open ? '0' : '1')};
    }
    :nth-child(3) {
      ${({ open }) => open && seconOpenAnimation}
    }
  }
`;

const firstOpenKeyframe = keyframes`
  50% {
    transform: translateY(6px) rotate(0);
  }
  100% {
    transform: translateY(6px) rotate(45deg);
  }
`;

const secondOpenKeyframe = keyframes`
  50% {
    transform: translateY(-6px) rotate(0);
  }
  100% {
    transform: translateY(-6px) rotate(-45deg);
  }
`;

const firstCloseKeyFrame = keyframes`
  50% {
    transform:translateY(0) rotate(-45deg);
  }
  100% {
    transform:translateY(-6px) rotate(-45deg)  ;
  }
`;

const firstOpenAnimation = css`
  animation: 0.3s linear ${firstOpenKeyframe} forwards;
`;

const seconOpenAnimation = css`
  animation: 0.3s linear ${secondOpenKeyframe} forwards;
`;

const firstCloseAnimation = css`
  animation: 0.3s linear ${firstCloseKeyFrame} forwards;
`;

export default StyledBurger;

When the menu is closed, I wanted to reverse the animation that occurred after the first click. I attempted a conditional rendering of animation keyframes based on the open prop, but encountered an issue where the animation was immediately triggered upon page load as it initially evaluated to false. I am currently exploring ways to resolve this and implement the opposite animation when the icon is unclicked. Any suggestions would be greatly appreciated!

Answer №1

With just a few adjustments, everything should function correctly.

  • Utilize the state to toggle open and pass it as a prop to your styled component.
  • Use a ternary operator for animation (instead of just &&)
    ${({ open }) => (open ? firstOpenAnimation : firstCloseAnimation)}
    .
  • Add the missing second close animation implementation.

A working version of your code can be found here

Functional code snippet

const StyledBurger = styled.button`
  display: flex;
  flex-direction: column;
  justify-content: center;
  border: 0;
  background-color: red;
  border-radius: 2.7px;
  cursor: pointer;
  height: 30px;
  div {
    width: 27px;
    height: 3px;
    margin: 1.5px;
    transition: all 0.2s linear;
    border-radius: 1.4px;
    background-color: white;
    :first-child {
      ${({ open }) =>
        open !== null && (open ? firstOpenAnimation : firstCloseAnimation)}
    }
    :nth-child(2) {
      opacity: ${({ open }) => (open ? "0" : "1")};
    }
    :nth-child(3) {
      ${({ open }) =>
        open !== null && (open ? seconOpenAnimation : secondCloseAnimation)}
    }
  }
`;

const firstOpenKeyframe = keyframes`
  50% {
    transform: translateY(6px) rotate(0);
  }
  100% {
    transform: translateY(6px) rotate(45deg);
  }
`;

const secondOpenKeyframe = keyframes`
  50% {
    transform: translateY(-6px) rotate(0);
  }
  100% {
    transform: translateY(-6px) rotate(-45deg);
  }
`;

const firstCloseKeyFrame = keyframes`
  50% {
    transform:translateY(0) rotate(-45deg);
  }
  100% {
    transform:translateY(0) rotate(0)  ;
  }
`;
const secondCloseKeyFrame = keyframes`
  50% {
    transform:translateY(0) rotate(-45deg);
  }
  100% {
    transform:translateY(0) rotate(0)  ;
  }
`;

const firstOpenAnimation = css`
  animation: 0.3s linear ${firstOpenKeyframe} forwards;
`;

const seconOpenAnimation = css`
  animation: 0.3s linear ${secondOpenKeyframe} forwards;
`;
const secondCloseAnimation = css`
  animation: 0.3s linear ${secondCloseKeyFrame} forwards;
`;

const firstCloseAnimation = css`
  animation: 0.3s linear ${firstCloseKeyFrame} forwards;
`;
export default function App() {
  const [open, setOpen] = useState(null);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <StyledBurger onClick={() => setOpen(prev => !prev)} open={open}>
        <div></div>
        <div></div>
        <div></div>
      </StyledBurger>
    </div>
  );
}

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

Tips for incorporating in/out animations with a container for the Slide feature:

I need help creating a component that will slide to the right when mounted, and to the left when unmounted. The Slide component should be contained in a div with the class "profile-slide-container", but I'm unsure how to achieve this. Below is the co ...

Create containers on the right side using HTML

Struggling to align the small boxes on the right side while keeping the left boxes from blending under them? I've attempted various solutions, but each time it seems like the code breaks. By the way, I utilized a generator to create the grid. Here is ...

Hierarchy-based dynamic breadcrumbs incorporating different sections of the webpage

Currently in the process of developing a dynamic breadcrumb plugin with either jQuery or Javascript, however I am struggling to implement functionality that allows it to change dynamically as the page is scrolled. The goal is to have a fixed header elemen ...

Round corners, images in the background, gradients in the background, and compatibility with Internet Explorer versions 8 and

I've encountered an issue where my div with border radius, background gradient, and background image are working fine in FireFox but not displaying correctly in IE8 or IE10. In IE8, the gradient and background image work properly, but as soon as I app ...

Changing the color of text in the navbar with Bootstrap 5

I'm completely new to coding and I'm currently trying to create a custom navbar using Bootstrap. I've been able to change the buttons, search bars, and background color, but I'm struggling to change the text color. I've tried vario ...

SvgIcon is not a recognized element within the JSX syntax

Encountering a frustrating TypeScript error in an Electron React App, using MUI and MUI Icons. Although it's not halting the build process, I'm determined to resolve it as it's causing issues with defining props for icons. In a previous pro ...

While not all attributes of the response object are null, there are certain object attributes that are displayed as null

My component is responsible for displaying data in a table that is fetched from Firestore. Although the data retrieved is complete, I am noticing that the settings attribute is often null when accessed in JSX. Occasionally, I do see the correct output, but ...

Ways to display HTML elements depending on the width of the window

Is there a way to create visually appealing hexagon containers for text that work well across all window widths? Currently, the design is only satisfactory at certain window sizes. I am looking to implement a solution where specific code will be displayed ...

Creating responsive data tables in HTML

My e-shop features a product description block using the code snippet below. While it looks great on larger screens, such as a 17" desktop monitor, it appears poorly on smaller smartphone screens. Friends have suggested that I learn Bootstrap/Flexbox and ...

The scrolling speed of my news div is currently slow, and I am looking to increase its

This is the news div with bottom to top scrolling, but it is slow to start scrolling. I want to increase the speed. The current behavior is the div appears from the y-axis of the system, but I want it to start exactly where I define. The scrolling div is ...

Unable to get the div to properly follow when scrolling, even when using the fixed position attribute

My webpage is divided into two sections - left and right. I've used divs to create the left navigation and right content. However, when scrolling down the page, only the right portion scrolls while the left navigation remains fixed. I'm looking ...

Problem with loading images on an HTML webpage

https://i.sstatic.net/aQ0oD.png After refreshing the page, I'm encountering an issue where the image is not loading. However, if I switch tabs from MEN to WOMEN and back again, the image will display without any problem. Even making changes in the CS ...

Prevent parentheses from being displayed in R's reactable group aggregation

I am utilizing R's reactable package to create a datatable that is grouped by a specific variable. The table initially displays collapsed with the ability to expand and show sub-rows. However, I noticed that each collapsed row title includes a set of ...

Focus loss occurs when the state changes in a Custom Tooltip containing an MUI TextField

Currently, I am utilizing MUI and have implemented a custom Tooltip for one specific TextField within my form. The issue arises when I start typing in this particular TextField as it loses focus immediately. Consequently, the state of the value in my formD ...

JavaScript Birthday Verification: Regular Expression Pattern

I am currently testing out JavaScript form validation. The information provided is not being sent to any database, it's just for format testing purposes. All the regex checks are functioning correctly apart from birth. It seems like there may be an i ...

Format six images in a horizontal row for desktop and ensure they resize appropriately for mobile viewing

I am looking to arrange images of different sizes in rows depending on the device orientation. For desktop, I want 6 images with proper margins, 2 images in a line for portrait, and 4 images for landscape orientation. Any extra images should be placed in a ...

Coordinates of HTML elements corners after rotation

Seeking to obtain the XY coordinates of an HTML element in order to accurately calculate the physical distance, in pixels, from the corners of a rotated element relative to another element. In this code snippet, I am attempting to determine the distance f ...

Methods for creating overlapping background images in breadcrumbs

I am having trouble with making the breadcrumb frame overlap the inside images. I attempted using z-index: -1, but it causes the images to disappear. Please refer to the attached image for a better understanding. Thank you. .breadcrumb { margin-botto ...

Tips for adjusting the thickness of the next/prev icons in the Bootstrap carousel

Exploring the world of HTML/CSS and Bootstrap. There's a carousel with these previous/next buttons : <button class="carousel-control-prev" type="button" data-bs-target="#testimonials-carousel" data-bs-slide="prev ...

Ways to position 4 containers next to each other using CSS

I need help aligning 12 buttons in a specific layout: 4 buttons on the top and 3 on each side to total 12 buttons. Also, I want 2 additional buttons at the top left (Div/Mult) and top right (Add/Sub). Below, you'll find the HTML and CSS documents. If ...