0

I started to write my own design system with styled-components. But I'm struggling with making my components responsive.

// Container.tsx
import React from 'react'
import styled from 'styled-components'

import { media } from '../utils/mediaQuery'

export const StyledContainer = (isFluid: boolean) => {
  return styled(
    ({ element: Component, containerRef, ...props }) => {
      console.log(isFluid)
      return (
        <Component ref={containerRef} {...props}>
          {props.children}
        </Component>
      )
    }
  )`
    width: 100%; /* Fix component tight to center*/
    margin-right: auto;
    margin-left: auto;
    padding-right: ${({ theme }) => theme.container.paddingX}rem;
    padding-left: ${({ theme }) => theme.container.paddingX}rem;

    ${isFluid ? '' : media.xl`max-width: 1040px;`}
    ${isFluid ? '' : media.lg`max-width: 960px;`}
    ${isFluid ? '' : media.md`max-width: 720px;`}
    ${isFluid ? '' : media.sm`max-width: 576px;`}
  `
}

type CProps = {
  fluid?: boolean
  element?: React.Component | string
  children: React.ReactNode | React.ReactNode[]
}

const Container = React.forwardRef(
  (props: CProps, ref: React.Ref<HTMLElement>) => {
    const { fluid = false, children, element = 'div' } = props

    const BaseContainer = StyledContainer(fluid)

    return (
      <BaseContainer element={element} containerRef={ref}>
        {children}
      </BaseContainer>
    )
  }
)

export default Container
// ../../utils/mediaQuery
import {
  css,
  SimpleInterpolation,
  CSSObject,
  FlattenSimpleInterpolation,
} from 'styled-components'


export const breakpoints: { [key: string]: number } = {
  xl: 1200,
  lg: 992,
  md: 768,
  sm: 576,
  xs: 376
}

export const media = Object.keys(breakpoints).reduce(
  (
    accumulator: {
      [key: string]: (
        styles: CSSObject | TemplateStringsArray,
        ...interpolations: SimpleInterpolation[]
      ) => FlattenSimpleInterpolation
    },
    label: string
  ) => {
    const size = breakpoints[label]

    Object.assign(accumulator, {
      [label]: (
        styles: CSSObject | TemplateStringsArray,
        ...interpolations: SimpleInterpolation[]
      ) => css`
        @media screen and (min-width: ${size}px) {
          ${css(styles, ...interpolations)}
        }
      `
    })

    return accumulator
  },
  {}
)

Everything worked normally. All CSS was bundled into chunked files, there is no bugs or errors appearing on console. My expected result is the order of media queries' values, which component has max-width following to screen size. But my actual result was @media screen and (min-width: 576px) {...}, which was bundled from ${isFluid ? '' : media.sm`max-width: 576px;`}, overriding other queries. Have any ideas what happened?

1 Answers1

0

Try reversing the order of your media query definitions:

${isFluid ? '' : media.sm`max-width: 576px;`}
${isFluid ? '' : media.md`max-width: 720px;`}
${isFluid ? '' : media.lg`max-width: 960px;`}
${isFluid ? '' : media.xl`max-width: 1040px;`}

so then the resulting CSS would be:

media screen and (min-width: 576px) {...}

media screen and (min-width: 720px) {...}

media screen and (min-width: 960px) {...}

media screen and (min-width: 1040px) {...}

For CSS media queries, you'll want the smallest min-width definition first.

Whereas before you had min-width: 576px last, which means any screen above 576px would take on that style.

This Stackoverflow answer explains why.

james
  • 5,006
  • 8
  • 39
  • 64