styled-components Style Objects

Date published: 13-Aug-2020
1 min read / 181 words
Author: Paul Scanlon

React
JavaScript
styled-components

styled-components optionally supports writing CSS as JavaScript objects instead of strings. This is particularly useful when you have existing style objects and want to gradually move to styled-components.

// Static object
const Box = styled.div({
background: "palevioletred",
height: "50px",
width: "50px",
})
// Adapting based on props
const PropsBox = styled.div((props) => ({
background: props.background,
height: "50px",
width: "50px",
}))
...
<div>
<Box />
<PropsBox background="blue" />
</div>
...

Destructuring props

A style object supports destructuring of props

// Destructure afer initial styles
const PropsBox = styled.div(
{
height: "50px",
width: "50px",
borderStyle: "solid",
borderWidth: "4px",
backgroundColor: "blue",
},
({ borderColor }) => ({
borderColor: borderColor,
})
)
...
<div>
<PropsBox borderColor="darkblue" />
</div>
...

Decendent/Sibling selectors

A style object can reference its decendent by component

// Decendent selector by component
const DecendentBox = styled.div({
backgroundColor: "blue",
height: "50px",
width: "50px",
borderStyle: "solid",
borderWidth: "4px",
})
const Box = styled.div({
display: "inline-block",
padding: "10px",
background: "palevioletred",
[`> ${DecendentBox}`]: {
borderColor: "darkblue",
},
})
...
<div>
<Box>
<DecendentBox />
</Box>
</div>
...

A style object can reference its decendent by a className

// Decendent selector by class
const DecendentBox = styled.div({
backgroundColor: "blue",
height: "50px",
width: "50px",
borderStyle: "solid",
borderWidth: "4px",
})
const Box = styled.div({
display: "inline-block",
padding: "10px",
background: "palevioletred",
"> .decendent-box": {
borderColor: "darkblue",
},
})
...
<div>
<Box>
<DecendentBox className="decendent-box" />
</Box>
</div>
...

A style object can reference its sibling(s) by a className.

child selector (>), adjacent sibling selector (+) and general sibling selector (~) work in the same way

// Adjacent Sibling selector by class
const SiblingBox = styled.div({
backgroundColor: "blue",
height: "50px",
width: "50px",
borderStyle: "solid",
borderWidth: "4px",
borderColor: "darkblue",
})
const Box = styled.div({
height: "50px",
width: "50px",
background: "palevioletred",
"+ .sibling-box": {
borderColor: "dodgerblue",
},
})
...
<div>
<Box />
<SiblingBox className="sibling-box" />
<SiblingBox className="sibling-box" />
</div>
...

A style object can reference its sibling(s) by component.

child selector (>), adjacent sibling selector (+) and general sibling selector (~) work in the same way

// Adjacent sibling selector by component
const SiblingBox = styled.div({
backgroundColor: "blue",
height: "50px",
width: "50px",
borderStyle: "solid",
borderWidth: "4px",
borderColor: "darkblue",
})
const Box = styled.div({
height: "50px",
width: "50px",
background: "palevioletred",
[`+ ${SiblingBox}`]: {
borderColor: "dodgerblue",
},
})
...
<div>
<Box />
<SiblingBox />
<SiblingBox />
</div>
...

Pseudo-classes

A style object can reference pseudo-classes and decendents with pseudo selectors

const Label = styled.label({
position: "relative",
cursor: "pointer",
})
// Pseudo selector
const Check = styled.span({
position: "absolute",
top: "0px",
left: "0px",
height: "25px",
width: "25px",
borderWidth: "4px",
borderStyle: "solid",
borderColor: "dodgerblue",
background: "white",
":hover": {
borderColor: "lightskyblue",
},
})
// Pseudo selector by component
const Input = styled.input({
position: "absolute",
opacity: 0,
height: "0px",
width: "0px",
[`:checked + ${Check}`]: {
background: "blue",
},
})
...
<div>
<Label>
<Input type="checkbox" />
<Check />
</Label>
</div>
...

Spread operator

With a style object you can take advantage of JavaScript's spread syntax

// JavaScript object for common styles
const fontStyles = {
fontFamily: "monospace",
fontSize: "1rem",
lineHeight: "1rem",
color: "palevioletred",
margin: "0px",
}
// Spread common styles across components
const H1 = styled.h1({
...fontStyles,
fontSize: "2.5rem",
lineHeight: "2.5rem",
})
const P = styled.p({
...fontStyles,
color: "rgb(243, 182, 97)",
})
...
<div>
<H1>h1. heading</H1>
<P>p. paragraph</P>
</div>
...

Unitless CSS values

The style object can work with both numbers and string values. Just like styled tagged template literals, number values are assumed as px unless used where unitless values are typically accepted such as lineHeight and flexGrow.

// height and width as number
const NumberBox = styled.div({
background: "palevioletred",
height: 50,
width: 50,
})
// height and width as string
const StringBox = styled.div({
background: "blue",
height: "50px",
width: "50px",
})
...
<div>
<NumberBox />
<StringBox />
</div>
...

With TypeScript

The style object can work with TypeScript interfaces.

interface IPropsBox {
backgroundColor: string;
}
const PropsBox = styled.div<IPropsBox>({
height: '50px',
width: '50px',
borderStyle: 'solid',
borderWidth: '4px',
backgroundColor: 'blue',
},
({ borderColor }) => ({
borderColor: borderColor,
}));
...
<div>
<PropsBox borderColor="darkblue" />
</div>
);


If you've enjoyed this post I'd love to hear from you: @PaulieScanlon