Take styled-components to the next level
Tags: design, javascript, react, styledsystem
Published: 2022-07-22 20:35:00

Most of us have been aware of CSS-in-JS libraries since around 2015 and they have come a long way since! The top libraries that most people are familiar with are `styled-components` and `emotion`. They have mostly the same API and syntax and for this article, I won’t go into the differences between the two.

One of the main benefits that CSS-in-JS (for this article I’ll mainly reference `styled-components`) provides is the ability to write, quite obviously, CSS in javascript files. This is usually either through using a template string like:

```jsx
import { styled } from 'styled-components'

const Container = styled.div`
  background-color: white;
`
```

…as well as using a Javascript object:

```jsx
import { styled } from 'styled-components'

const Container = styled.div({
  backgroundColor: 'white',
})
```

This especially changed the game for the React world because now we can pass props to these styled components for contextual use cases

```jsx
import { styled } from 'styled-components'

const Container = styled.div((props) => ({
  backgroundColor: props.mode === 'light' ? 'white' : 'black',
}))

const MyComponent = () => {
  const { mode } = useThemeContext()
  
  return <Container mode={mode}>...</Container>
}
```

No more messing with classes and CSS (Sass/Less) files!

### Pitfalls

The flexibility of `styled-components` is great for developers to get started using it, but it can also create problems for large teams that may not have the best communication standards and best practices in place. Some developers may use the template literals, while others use CSS objects. This can cause some inconsistency in how design tokens are utilized and can cause one-off values that can make it difficult to update according to a set theme. This is where `styled-system` can help with giving CSS-in-JS more structure to help build a robust design system component library.

### What is `styled-system`

> Styled System lets you quickly build custom UI components with constraint-based style props based on scales defined in your theme.
> 

It allows for style props to be added directly to styled-components so that it doesn't need to be done manually. It also hooks into the theme in a type-safe way to take the guesswork out of what type of variable should be used according to the design system.

```jsx
<Box
  fontSize={4} // 4th index in theme fontSizes = ['0.5rem', 0.75rem', '1rem', 1.5rem']
  fontWeight='bold'
  p={3} // 3rd index in theme spacing = ['0px', '2px', '4px', '8px', '16px']
  mb={[ 4, 5 ]}
  color='white'
  bg='primary' // contextual color value at the theme level
>
  Hello World
</Box>
```

### How does this help?

Why would we want to add something like this to the way we already do things with `styled-components`?

### First-class theming

When working with `styled-components`, we often use the `styled` function that is offered. With this function, we can define what type of HTML element we want to use. To take that even further, we can even add styles to any React component (granted it accepts and passes the `className` to the component's underlying elements).

```jsx
import { styled } from 'styled-components'
import { colors } from 'design'

const Container = styled.div`
  background-color: white;
  color: ${colors.green4};
  border-color: ${(props) => props.theme.primaryColor}
`
```

As you can see, we have **three** different ways to define a color. And the right way may not be so apparent, especially to someone who is not familiar with the code base. This ambiguity allows for theming to be a second-class citizen and for it to seem acceptable to have many one-off values (in a few cases it can be acceptable). So what if there is only one real way to define this color (two if you'd like to consider the "escape hatch" `css` prop as an option)?

```jsx
<Box
  {/* with Typescript we can restrict these values to what is a part of the theme */}
  bg="white"
  color="green3"
  borderColor="primaryColor"
>
  ...
</Box>
```

These three values come directly from the theme object if it were to look like this:

```jsx
export const theme = {
  colors: {
    white: '#fff',
    green3: '#1ea676',
    primaryColor: '#1ea676',
  }
}
```

### Responsiveness

We live in a world that has so many devices and devices sizes to access the web. And because all those different devices have their own screen sizes, our site needs to be responsive to be able to provide an elegant user interface for each of those devices. To do so we utilize `media queries` and show styling based on the different device screen sizes. The way we currently do that with `styled-components` looks like this:

```jsx
import { styled } from 'styled-components'
import { size, mediaQueries } from 'design'

const Container = styled.div`
  margin-top: ${size.sm}px; /* sometimes we forget about handling the mobile size */

  ${mediaQueries.forTabletVerticalUp} {
    margin-top: ${size.md}px;
  }

  /* sometimes we forget about handling the large desktop size */
`
```

With `styled-system`, almost every prop comes with a scaled value option - meaning we can easily give a value for each defined breakpoint. Let's say our theme breakpoints are set up like

```jsx
export const theme = {
  breakpoints: ['400px', '600px', '900px', '1200px'], // mobile, tablet, desktop, large desktop
  size: {
    0: '0px',
    sm: '4px',
    md: '8px',
    lg: '16px',
  }
}
```

We can write our components like below without having to wrap `styled` around it and manually set the media queries

```jsx
<Box
  marginTop={['sm', 'md']} // 'sm' for mobile, 'md' for everything larger
>
  ...
</Box>
```

### Atomic Design

Atomic Design is a design concept that web elements should build upon each other like atoms, molecules, organisms, and so on.

![https://bradfrost.com/wp-content/uploads/2013/06/atomic-design.png](https://bradfrost.com/wp-content/uploads/2013/06/atomic-design.png)

This concept is a bit tough to achieve with `styled-components` alone because, with the `styled` method, we tend to "extend" components constantly just to add some styling. When we extend these base components like that, we are essentially trying to recreate the atom with different protons and neutrons and to any newcomer, it looks like a new atom.

With  `styled-system`, we maintain the base components as much as possible by relying on [specialization](https://reactjs.org/docs/composition-vs-inheritance.html#specialization) - which means adding different prop values to add a specialization to a more generic component. This way as the team grows and new developers look at the codebase, it is more apparent what the underlying HTML markup is and what the intentions are.

To explain an example:

- a `button` element is an **atom**
- a `styled-system` + `styled-component` Button is a **molecule**
- components that ingest a Button would be an **organism**
- a collection of **organisms** that make up an experience would be a **template**
- **templates** make up a **page**

### Downsides to `styled-system`

**Not actively maintained**

Unfortunately, the `styled-system` repo on Github is not actively maintained but that doesn’t mean it’s useless. This just means that no further improvements or fixes will be made and if the tool does not need any fixes then that’s okay. `styled-system` is super lightweight and does not have any real (shipped) dependencies. Essentially it’s a collection of methods that can be added to either `styled-components` or `emotion` to enable various style props. This is why the package size is `~150kb`, which is pretty small!

**Other alternatives are more robust**

The creators of `styled-system` also have created [theme-ui](https://theme-ui.com/), which is a more full-fledged (and more fledge-ness coming soon) than `styled-system`. The main difference between the two is that `theme-ui` is opinionated to use `emotion` under the hood and is not compatible with `styled-components` ([read more](https://theme-ui.com/guides/styled-components)). Both of them do use the System UI specification as well as a few more that you can [find here](https://system-ui.vercel.app/).

## Conclusion

I believe the concept of `styled-system` is huge, especially when paired with Typescript, in developing a robust design system and component library. It allows for defining the styles inline with the component instead of somewhere else, which can make writing CSS less painful - especially if Typescript can tell you variables you have to pick from and it doesn’t give you an option to veer away from it unless you really try hard. And because it allows you to make your components more strict when it comes to what design token options it accepts, the more resilient to change they are as those tokens can be changed in one place and that change updates all the components respectively since they *have* to use the tokens and not one-off values.

So, if you work with a large group and would like to avoid rewriting the same styles and CSS values, I would highly recommend `styled-system` or any of the other libraries using the System UI specification. #happycoding