If you've used a component with style functions from the styled-system library you might want to bring the API to all your projects. If that's the case we'll walk through how to build a button component using Styled System.
In this tutorial we'll go over using a styled function, theming providers, and variants.
For this example we'll be using Emotion, but any CSS-in-JS library will work.
yarn add styled-system @emotion/core @emotion/styled emotion-theming
Next, we create a basic styled button
import styled from '@emotion/styled'
const Button = styled('button')({})
And add some basic styles:
const Button = styled('button')({
appearance: 'button',
border: 0,
outline: 0
})
Now that we have some styling it's time to introduce some color. We can do that using the color
function from Styled System. We do this by passing it as another argument after the style object:
import styled from '@emotion/styled'
import { color } from 'styled-system'
const Button = styled('button')(
{
appearance: 'button',
border: 0,
outline: 0
},
color
)
This exposes to props to the Button component: color
and backgroundColor
so you can use the
component like so:
<Button color="white" backgroundColor="tomato">
Hello, world!
</Button>
Then, if we want to add defaults we can use defaultProps
:
Button.defaultProps = {
color: 'white',
backgroundColor: 'blue'
}
Now that we're setting colors on the Button component we want to use a ThemeProvider to use
a globally defined theme for all components. We can achieve that with emotion-theming
and
then wrapping the app. Styled System will automatically tie into the theme and return a value
if it exists.
import styled from '@emotion/styled'
import { ThemeProvider } from 'emotion-theming'
import { color } from 'styled-system'
const theme = {
space: [0, 2, 4, 8, 16, 32],
colors: {
blue: '#07c',
tomato: 'tomato',
purple: 'purple'
}
}
function Demo() {
return (
<ThemeProvider theme={theme}>
<Button>Styled System Button!</Button>
</ThemeProvider>
)
}
Similarly to color
, we can add in space
and fontSize
to add more style props to the Button.
import styled from '@emotion/styled'
import { ThemeProvider } from 'emotion-theming'
import { color, space, fontSize } from 'styled-system'
const Button = styled('button')(
{
appearance: 'button',
border: 0,
outline: 0
},
color,
space,
fontSize
)
Button.defaultProps = {
color: 'white',
backgroundColor: 'blue',
px: 4,
py: 3,
fontSize: 2
}
const theme = {
space: [0, 2, 4, 8, 16, 32],
fontSizes: [14, 16, 18, 24, 32],
colors: {
blue: '#07c',
tomato: 'tomato',
purple: 'purple'
}
}
function Demo() {
return (
<ThemeProvider theme={theme}>
<Button>Styled System Button!</Button>
</ThemeProvider>
)
}
Now the button can have padding
, margin
, and fontSize
set.
For buttons it's common to have a need for a variant
prop which can
determine interrelated styles like color, box shadow, or anything else.
You can use a special buttonStyle
key in your theme and then import the
buttonStyle
function from styled-system.
import styled from '@emotion/styled'
import { ThemeProvider } from 'emotion-theming'
import { buttonStyle, space, fontSize } from 'styled-system'
const Button = styled('button')(
{
appearance: 'button',
border: 0,
outline: 0
},
buttonStyle,
space,
fontSize
)
Button.defaultProps = {
variant: 'primary',
px: 4,
py: 3,
fontSize: 2
}
const baseTheme = {
space: [0, 2, 4, 8, 16, 32],
fontSizes: [14, 16, 18, 24, 32],
colors: {
blue: '#07c',
tomato: 'tomato',
purple: 'purple'
}
}
const theme = {
...baseTheme,
buttons: {
primary: {
color: 'white',
backgroundColor: baseTheme.colors.blue
},
secondary: {
color: 'white',
backgroundColor: baseTheme.colors.purple
},
danger: {
color: 'white',
backgroundColor: baseTheme.colors.tomato
}
}
}
Now you can use primary
, secondary
, and danger
variants in your Button
like so:
<Button variant="danger">I'm tomato!</Button>
You can specify any number of other style variants using the variant
function.
With it you can specify custom keys in the theme object and even a custom prop.
Below is an example with a size
variant for Button:
import { buttonStyle, space, fontSize, variant } from 'styled-system'
const buttonSize = variant({
prop: 'size',
key: 'buttonSizes'
})
const theme = {
...baseTheme,
buttons: {
primary: {
color: 'white',
backgroundColor: baseTheme.colors.blue
},
secondary: {
color: 'white',
backgroundColor: baseTheme.colors.purple
},
danger: {
color: 'white',
backgroundColor: baseTheme.colors.tomato
}
},
buttonSizes: {
medium: {
fontSize: baseTheme.fontSizes[2],
padding: `8px 16px`
},
large: {
fontSize: baseTheme.fontSizes[4],
padding: `16px 32px`
}
}
}
Now you can change the button sizing like so:
<Button size="large">I'm large</Button>
If you want to add a border radius prop to your component you can first create
a radii
key in your theme.
const baseTheme = {
space: [0, 2, 4, 8, 16, 32],
fontSizes: [14, 16, 18, 24, 32],
colors: {
blue: '#07c',
tomato: 'tomato',
purple: 'purple'
},
radii: [0, 2, 4, 8]
}
Then you can add import the borderRadius
function from Styled System and
pass it to the Button component definition. You can also set a default so
that all buttons will take on the same border radius value.
import {
borderRadius,
buttonStyle,
space,
fontSize,
variant
} from 'styled-system'
// ...
const Button = styled('button')(
{
appearance: 'button',
border: 0,
outline: 0
},
borderRadius,
buttonStyle,
buttonSize,
space,
fontSize
)
Button.defaultProps = {
borderRadius: 2
}
Since Button uses the space
function from Styled System you can set custom margin or
padding. This is nice for adjacent buttons when you want to add a bit of margin on the
left of the second.
<Button>Button 1</Button>
<Button marginLeft={2}>Button 2</Button>
This is a great workflow for when you want to add spacing quickly and efficiently to a bit of markup. By default, it will ensure that design system scales will be used so that things remain consistent.
You can continue going further with other style functions for box-shadow
, line-height
,
and more.
See the Styled System table of functions
Using this approach you can create components that can do essentially anything.
Especially with the variant
prop you can create enums that map to groups of
styling which ensure a great developer experience.