Search code examples
cssreactjsstyled-components

CSS Conditional styling if Component is of max-width: 600px


not the screen I want to apply this if the component is of width 600px I'm trying to change some styling if the component is in a Modal I only want to change flex-direction from column to row-reverse so I can't see a point in doing another component my code

const wrapper = styled.div`
  display: flex;
  flex-direction: column;
  @media screen and (max-width: 600px) {
      flex-direction: row-reverse;
  }
}
`

This obviously doesn't since it is checking the screen


Solution

  • Using CSS @container queries

    Using container queries requires creating a parent container element that the Wrapper component can be rendered into. This is supported in styled-components@6.

    Example:

    const Container = styled.div`
      container-type: inline-size;
    `;
    
    const Wrapper = styled.div`
      display: flex;
      flex-direction: column;
    
      @container (max-width: 600px) {
        flex-direction: row-reverse;
      }
    `;
    

    Usage:

    <Container>
      <Wrapper>
        ...wrapped content...
      </Wrapper>
    </Container>
    

    Or a bit more succinct using an anonymous inline function:

    const Div = styled((props) => (
      <div className="container">
        <div {...props} />
      </div>
    ))`
      display: flex;
      flex-direction: column;
    
      @container (max-width: 600px) {
        flex-direction: row-reverse;
      }
    
      .container:has(> &) {
        container-type: inline-size;
      }
    `;
    

    Usage:

    <Wrapper>
      ...wrapped content...
    </Wrapper>
    

    Using Javascript to measure the DOM

    If your project or environment doesn't support container queries then you will need to measure the div HTML node to check its width to see if it is less than or equal to 600 pixels or not.

    Here's an example implementation using a wrapper component to do the measuring of the styled div element:

    Custom hook to measure DOMNode by React ref and return its width.

    const useElementWidth = () => {
      const ref = useRef();
      const [width, setWidth] = useState();
    
      useEffect(() => {
        const handler = () => {
          setWidth(ref.current.getBoundingClientRect().width);
        };
    
        handler();
    
        window.addEventListener("resize", handler, true);
        return () => {
          window.removeEventListener("resize", handler, true);
        };
      }, []);
    
      return {
        ref,
        width
      };
    };
    

    Wrapper to attach ref and set CSS classname for when the "media query" condition is met.

    import { clsx } from "clsx";
    
    const Wrapper = (props) => {
      const { ref, width } = useElementWidth();
    
      return (
        <InnerWrapper
          ref={ref}
          className={clsx({ mobile: width <= 600 })}
          {...props}
        />
      );
    };
    

    The original div styled component.

    const InnerWrapper = styled.div`
      display: flex;
      flex-direction: column;
    
      &.mobile {
        flex-direction: row-reverse;
      }
    `;
    

    Element under 600px

    enter image description here

    Element over 600px

    enter image description here

    Element in parent element with max-width of 700px

    enter image description here