Creating Clean Responsive Apps with React and Styled-System
By: Manato Kuroda
Tags: Design Systems, Styled Components, React, Web Development, JavaScript

Creating Clean Responsive Apps with React and Styled-System

A simple tutorial of building a responsive React app using Styled-System based on the Design System.

Styled System

Example Repo

You can view the final working example at:

Component design systems let a team collaborate and provide users a consistent visual and functional UI across different applications. Consistency has four types, visual consistency, functional consistency, internal consistency and external consistency at the UX design principle and I focus on visual consistency in this article.

Front-end Development Issue

Front-end development issues have been around for a while, it’s like:

Inconsistent style

A lot of duplicated UI components

A lot of unnecessary time to design and develop

These happen because each designer has their own style and tone. Each developer creates their own components without noticing it has been developed already.

To solve it, keeping components consistent is the most important even if multiple designers and developers involved in the same project. Designers need to know how to design and to share it with others. Developers need to make reusable components across every part of the project based on a style guide.

Most importantly, on the user’s side, a successful component system provides less confusion, better navigation of the product and better overall satisfaction. Users will learn faster how to use your product.

Design System

Design System

A design system basically includes a collection of the assets and components, like a library of design patterns, rules and UX guidelines to build a product and also provides an evolving map of a brand’s territories with direction to create a new product. It should be centralized and always evolving to keep consistent.

On the developer’s side, to achieve true collaboration with a design system, bridging the gap between design and development through components on the codebase is the key.

Styled System

Styled-System is one of the options to achieve collaboration. It’s designed with a set of utilities that map props to a design system and also has a bunch of API with functions for CSS. It works with CSS-in-JS libraries such as styled-component, emotion and even supports Vue.js.

Let’s take a look at how it works.

Install styled-system and styled-component :

$ yarn add styled-system styled-components

And add some styled-components in your App.js :

import React from "react"

import { space, width, fontSize, color } from 'styled-system';

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

import theme from './theme';

const App = () => {

return (

<ThemeProvider theme={theme}>

<Box>

This is a Box

</Box>

</ThemeProvider>

)

} const Box = styled.div`

${space}

${width}

${fontSize}

${color}

`; export default App

ThemeProvider is a built-in provider in styled-components which has full theming support. This component provides a theme to all React components underneath itself through the context API. By using this, styled-system extends the feature as more reusable components. ${space} , ${width} , ${fontSize} and ${color} are the functions to provide a certain style.

Let’s add some style:

const App = () => {

return (

<ThemeProvider theme={theme}>

<Box bg="orange" fontSize={24} width={200} p={20} m="50px auto">

This is a Box

</Box>

</ThemeProvider>

)

}

You’ll get a box like this:

This is a Box

As you can see, some props are passed to Box and styles are applied. This is how styled-system works. You don’t need to write a CSS inside the styled-component’s template literal. (If you want to know the magic of styled-components do in a template literal, check out here)

Other than the above example, there are a bunch of API.

Now that you know how to use it. Next thing you can apply your design system to the components by defining theme .

Let’s edit theme.js :

const theme = {

space: [0, 4, 8, 16, 32, 64, 128, 256, 512],

fontSizes: [12, 14, 16, 20, 24, 36, 48, 80, 96],

fontWeights: [100, 200, 300, 400, 500, 600, 700, 800, 900],

width: [16, 32, 64, 128, 256],

heights: [16, 32, 64, 128, 256],

colors: {

black: '#000',

gray: ' #777',

'dark-gray': '#333',

'light-gray': '#eee',

},

// .... and other styles

}

export default theme

This is supposed to be your own design system but for now, we defined it like above.

fontSizes has 12, 14, 16, 20... sizes and let’s suppose apply font-size: 12px to the Box component:

<Box bg="orange" fontSize={0} width={200} p={20} m="50px auto">

This is a Box

</Box>

0 is passed not 12 , which means that you need to pass the index number of fontSizes array. In this case 0 means 12 and 1 means 14 and so on.

But you can make it more declarative in theme.js like this:

const theme = {

space: [0, 4, 8, 16, 32, 64, 128, 256, 512],

fontSizes: {

small: 14,

medium: 16,

large: 18

},

...

}

export default theme

And edit it App.js :

<Box bg="orange" fontSize="small" width={200} p={20} m="50px auto">

This is a Box

</Box>

The way to style component is reasonable and maintainable because as a developer you don’t actually need to think about what size should be applied in the box, just follow the style guideline and pass small to it.

Responsive Design with Styled System

Next, I want to demonstrate how to make components responsive. In responsive design, you basically use Media Queries in CSS. Styled-system provides its own responsive system, which offers a convenient shorthand syntax for adding a responsive style with a mobile-first approach and also alternatively provides you with breakpoints to define your own responsive value.

Let’s add it in theme.js :

const theme = {

...

breakpoints: {

xs: '0',

sm: '600px',

md: '960px',

lg: '1280px',

xl: '1920px',

},

}

I follow the Material Design breakpoints value. These values should work like this:

|0px 600px 960px 1280px 1920px

|xs sm md lg xl

|--------|--------|--------|--------|-------->

| xs | sm | md | lg | xl

Let’s make Box responsive:

const App = () => {

return (

<ThemeProvider theme={theme}>

<Box

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

width={{ xs: 200, sm: 400, md: 600, lg: 1000 }}

p={20}

m="50px auto">

This is a Box

</Box>

</ThemeProvider>

)

}

Responsive box

It’s more readable and helps you understand what value should be used in each device.

Typography

Material Design Typography

Font size is important when you design. As a designer, it’s important to organize a scalable design system and for typography as well, to need to set up heading levels and to create a hierarchy for clear visibility.

As a developer, it’s too much hassle to apply each font to each device even though only mobiles. But in styled-system, you don’t need to write a specific font size to every component.

Let’s suppose some heading level in theme.js :

const theme = {

fontSizes: {

small: 14,

medium: 16,

large: 18, h5: 12,

h4: 18,

h3: 20,

h2: 24,

h1: 30,

},

}

Alternatively, there is another approach:

const theme = {

fontSizes: {

small: 14,

medium: 16,

large: 18, h5: {

sm: 12,

md: 14,

lg: 16,

},

h4: {

sm: 16,

md: 18,

lg: 22,

},

h3: {

sm: 19,

md: 22,

lg: 28,

},

h2: {

sm: 20,

md: 22,

lg: 24

},

h1: {

sm: 24,

md: 26,

lg: 30

}

}

}

A system could have a more limited set of heading levels and specify size per each device.

You can dig into more details at Size in Design Systems, which is a really awesome post.

Responsive Layout

As mentioned above, width can be responsive and you can define responsive layout. But if you prefer layout based on a grid system, alternatively Material-UI has its own Grid layout.

Let’s install Material-UI:

yarn add @material-ui/core

And add it to App.js :

const App = () => {

return (

<ThemeProvider theme={theme}>

<Grid container spacing={2}>

<Grid item xs={12} md={3}>

<Box

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box>

</Grid>

<Grid item xs={12} md={3}>

<Box

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box>

</Grid>

<Grid item xs={12} md={3}>

<Box

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box>

</Grid>

</Grid> </ThemeProvider>

)

}

Responsive with Material-UI

Grid component can take breakpoints like xs , sm , md lg and xl and should be matched with your breakpoints.

Separate Component into PC and Mobile

Next, what if you want to separate HTML into pc and mobile. Then you can archive it by using Hidden .

Let’s create a component that toggles visibility value. I call it Media component and it takes two props, pc and mobile .

Before that install lodash :

yarn add lodash

And add Media.js :

// Media.js import React from 'react'

import HiddenCss from '@material-ui/core/Hidden/HiddenCss'

import omit from 'lodash/omit' const Media = (props) => {

const hiddenProps = omit(props, 'mobile', 'pc') switch (true) {

case Boolean(props.mobile):

return (

<HiddenCss {...hiddenProps} smUp>

{props.children}

</HiddenCss>

)

case Boolean(props.pc):

return (

<HiddenCss {...hiddenProps} xsDown>

{props.children}

</HiddenCss>

)

default:

return <>{props.children}</>

}

} export default Media

And apply it to App.js :

const App = () => {

return (

<ThemeProvider theme={theme}>

<Media pc>

<p>Hi I'm from PC</p>

</Media>

<Media mobile>

<p>Hi I'm from mobile</p>

</Media>

</ThemeProvider>

)

}

Responsive with Media

Now it clearly separates HTML into pc and mobile on component levels.

Composing style

There are a bunch of style functions provided by styled-system. You might find it tough to add them to every component. To solve it, the wrapper component, which has style functions can be used like this:

// utils/styledSystem.js import styled from 'styled-components'

import {

compose,

space,

color,

layout,

typography,

flexbox,

border,

background,

position,

grid,

shadow,

width,

minWidth,

height,

minHeight,

} from 'styled-system'

const styledSystem = (tag) => {

return styled(tag)(

compose(

space,

color,

layout,

typography,

flexbox,

border,

background,

position,

grid,

shadow,

width,

minWidth,

height,

minHeight,

),

)

} export default styledSystem

And wrap styled-component with it:

import React from "react"

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

import theme from './theme';

import Grid from '@material-ui/core/Grid';

import styledSystem from './utils/styledSystem' const App = () => {

return (

<ThemeProvider theme={theme}>

<Heading fontSize={{ xs: 'h1' }}>Title here</Heading>

<Grid container spacing={2}>

<Grid item xs={12} md={3}>

<Box

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box>

</Grid>

<Grid item xs={12} md={3}>

<Box2

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box2>

</Grid>

<Grid item xs={12} md={3}>

<Box3

bg={{xs: 'orange', sm: 'grey'}}

fontSize={{xs: 'small', sm: 'medium', md: 'large'}}

p={20}

m="50px auto">

This is a Box

</Box3>

</Grid>

</Grid>

</ThemeProvider>

)

} const Box = styledSystem(styled.div``)

const Box2 = styledSystem(styled.div``)

const Box3 = styledSystem(styled.div``)

const Heading = styledSystem(styled.h1``) export default App

All style functions are applied with components.

Another option would be using Rebass, which is a React UI components built with styled-system and highly compatible with the Design System.

Conclusion

You can mostly be prepared to build responsive components. Every component should be readable, maintainable and scalable. Making responsive is hard but it’s worth it for great user experience.

I hope it’ll help you.

You can view the final working example at: