16

I can get media queries to work properly inside a regular styled-components component however when I attempted to use it in a keyframe (via import from styled-components) it does not seem to work at all.

Attempting to get a div to animate to a specific position, but have that end position change when the window is < 800px, I've attempted this:

import styled, { keyframes } from 'styled-components';

// ... further down ...

const slideAnim = keyframes`
  100% {
    top: 20px;
    left: 30px;
  }

  @media (max-width: 800px) {
    top: 70px;
    left: 50px;
    }

`;

I have also tried putting the media query in the 100% block:

const slideAnim = keyframes`
  100% {
    top: 20px;
    left: 30px;

    @media (max-width: 800px) {
       top: 70px;
       left: 50px;
    }
  }
`;

I made a helpful interactive demonstration of what I am trying to achieve (problem code is at line 24): https://codesandbox.io/embed/fragrant-star-m71ct

Feel free to change the breakpoint variable from 800 if you need to.

Any help is appreciated!

Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
damon
  • 2,687
  • 8
  • 25
  • 35

3 Answers3

17

You could take a different approach, where your keyframes animation is instead defined as a function like this:

const slideAnim = (top, left) => keyframes`
  100% {
    top: ${ top };
    left: ${ left };
  }  
`;

The function accepts input parameters that dictate the destination coordinates of top and left for that animation's final keyframe.

In your stlyed component (ie Box1), you'd then invoke slideAnim() with specific coordinates to each breakpoint, to achieve different animation behavior per breakpoint:

/*
Declare responsive styling in the Box1 component, where destination
coordinates are specified for the animate on a per-breakpoint-basis
*/
const Box1 = styled.div`
  width: 20px;
  height: 20px;
  background-color: blue;
  position: absolute;
  left: -100px;
  top: 80px;

  &.slide {

    animation: ${slideAnim('20px', '30px')} 0.4s ease-in-out forwards;

    @media (max-width: ${breakpoint}px) {
      animation: ${slideAnim('70px', '50px')} 0.4s ease-in-out forwards;
    }    
  }
`;

In summary, the idea is to shift the responsive styling into your styled component (ie Box1), while defining a reusable function that contains the common/shared keyframes for each responsive breakpoint.

Here's a working example - hope that helps!

Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
  • 1
    Oh sweet! I wasn't even aware that I could define a styled component as a function in this way. This solves my problem and is even better than what I was trying to do since I can flexibly reuse that function for multiple animations (which I need to do for my real project). Thanks! – damon Aug 20 '19 at 03:34
  • 1
    @damon hey you're welcome - yep, the styled components library is pretty amazing :) – Dacre Denny Aug 20 '19 at 03:51
  • This is a different approach but this thing can be solved by normal approach. Cool anyways. A different approach is always good. – Dingus45191 May 21 '21 at 12:31
4

A more developed approach to use media queries with styled-components

firstly we define the screen size for each device:

const size = {
  mobile: "320px",
  tablet: "768px",
  laptop: "1024px",
  desktop: "2560px",
}

then we use css imported from styled-components to address media queries in relevant constants for each device

export const mobile = (inner) => css`
  @media (max-width: ${size.mobile}) {
    ${inner};
  }
`;
export const tablet= (inner) => css`
  @media (max-width: ${size.tablet}) {
    ${inner};
  }
`;
export const desktop= (inner) => css`
  @media (max-width: ${size.desktop}) {
    ${inner};
  }
`;
export const laptop= (inner) => css`
  @media (max-width: ${size.laptop}) {
    ${inner};
  }
`;

now lets say you are creating a styled div and you want to use media queries it. and based on the accepted answer to use keyframes

const slideAnim = (top, left) => keyframes`
  100% {
    top: ${ top };
    left: ${ left };
  }  
`;
const StyledDiv= styled.div`

 /* ...css */

    animation: ${slideAnim('20px', '30px')} 0.4s ease-in-out forwards;

   ${mobile(css`
     width:300px;
     animation: ${slideAnim('10px', '20px')} 0.1s ease-in-out forwards;
      /* ...css */
   `)};

   ${tablet(css`
     width:500px;
      /* ...css */
   `)};

    /*... and so on */
`;
Safi Habhab
  • 981
  • 10
  • 17
0

Use keyframes outside of all blocks. eg:

import { IconButton} from "@material-ui/core"; 
const CloseButton = styled(IconButton)`
  padding: 3px !important;
  outline: none;


  @media only screen and (min-width: 728px) {
    padding: 4px !important;
    margin:0
  }
`;
Dingus45191
  • 532
  • 1
  • 7
  • 19