How to use styled-system responsive props

3.1k views Asked by At

Can I use the styled-system to achieve something like this?

<MyComponent  
  backgroundImage={{
    default: "https://placekitten.com/380/80",
    sm: "https://placekitten.com/340/80"
  }}
/>

or this (because I know it can be done this way too with other "style props" such as width, but I prefer to use an object with keys):

<MyComponent  
  backgroundImage={[
    "https://placekitten.com/300/80",
    "https://placekitten.com/500/80"
  ]}
/>

I think the code examples above are self-descriptive and they follow the library's pattern but just to be clear, I'm mapping the values (image sources) to the breakpoints (default and next one up).

For example, this works out of the box:

<Box
  width={[
    default: 1,
    sm: 1/3,
  ]}
/>

The output is something like this:

.QWojd {
    width: 100%;
}

@media screen and (min-width: 24em) {
    .QWojd {
        width: 33.33333333333333%;
    }
}

I've been looking into the source code and this part here makes me think it should work with backgroundImage too:

BackgroundImageProps

Sadly, it doesn't work, and the result is a stringified object (or concatenated array values) in the CSS output.

I can't think of how the variant function would be useful here as people have suggested. I've tried to use the system function but I just can't understand the documentation. The ResponsiveValue type gives me a hint but I feel like crawling in the dark when I try to understand the internals.

Ultimately, I'd like to use the "breakpoints object" (or array) with whatever custom prop I feel like, like this:

<Box
  myProp={[
    default: 'foo',
    sm: 'bar',
  ]}
/>

Note: I've learned (from experience) that you can just use the "breakpoints array" version (without setting breakpoints in a theme and passing it to a provider) and that will map the value to the first 2 default breakpoints (not sure where they come from) but if you want to use an object with keys you need to use a ThemeProvider with a theme object with your own breakpoints.

Note 2: I can understand the styled-system documentation up to this point: https://styled-system.com/custom-props. When I arrive here I feel like this is what I'm looking for but I can't understand the example, the explanation confuses me even more and I can't find any examples on the web.

Note 3: Spectrum Chat has a styled-system channel and the library author is in there but sadly I haven't been able to send any messages there (constant network error)

Examples

2

There are 2 answers

0
Eduardo Grigolo On BEST ANSWER

Ok, so according to the docs (https://styled-system.com/custom-props/), in order to create a custom prop (or in this case, replace the existing one) you should use the system utility. Since I'm not a user of this library (styled-system), I'm not 100% sure that this is correct approach, but I tested on top of your example code and it seems to work as you wanted.

The component declaration (it also works with objects like you wanted) with an array:

<ResponsiveImageBox
  color="white"
  backgroundImage={[
    "https://placekitten.com/300/80",
    "https://placekitten.com/500/80"
  ]}
>
  Box 8
</ResponsiveImageBox>

with objects:

<ResponsiveImageBox
  color="white"
  backgroundImage={{
    default: "https://placekitten.com/300/80",
    sm: "https://placekitten.com/500/80"
  }}
>
  Box 8
</ResponsiveImageBox>

And this is the component code:

export const ResponsiveImageBox = styled(Box)(
  ({ myCustomProp }) => { 
    return css`
      ${system({
        backgroundImage: {
          property: "backgroundImage",
          transform: value => `url(${value})`
        }
      })}
    `
  });

As you can see on examples 4, 5 and 8 (https://stackblitz.com/edit/styled-system-mccqje?file=Examples.tsx), I also did it for the border-radius attribute with a simple prop renaming and just specifying what css attribute I wanted to change (property), so no need to add transform as the value will remain the same.

export const ExtendedBox2 = styled(Box)<ExtendedBoxProps>`
  background-position: center;

  ${system({
    myCustomProp: {
      property: "border-radius"
    }
  })}
`;

Have a look and see if this is what you were looking for! :)

0
JosephScript On

I know you already marked it as solved, and Eduardo's approach definitely works. However another way you can do it "out of the box" is to use aliases so that you can use objects instead of arrays (source: https://styled-system.com/responsive-styles/):

// theme.js
const breakpoints = ['40em', '52em', '64em', '80em']

// aliases
breakpoints.sm = breakpoints[0]
breakpoints.md = breakpoints[1]
breakpoints.lg = breakpoints[2]
breakpoints.xl = breakpoints[3]

export default {
  breakpoints,
}

// ResponsiveImageBox.js
<ResponsiveImageBox
  color="white"
  backgroundImage={{
    md: "https://placekitten.com/300/80",
    sm: "https://placekitten.com/500/80"
  }}
>
  Box 8
</ResponsiveImageBox>