I'd like to have a component, styled with Emotion, that takes props that ultimately control the styling. For example, consider a GridCol
component that has various props that change the padding and width (the width can be changed across different viewport widths).
I'd like to use an API like this:
<GridCol gutter size="2>
// or alternatively, like this:
<GridCol gutter size={{
m: 2,
l: 4
}}>
There are three things happening here:
gutter
is a boolean prop that adds some horizontal padding to the columnsize
prop can be a string or an object. If it is a string, we just add a few lines of CSS and we're good, however, if it is an object, we need to insert some media-queries based on a breakpoints object that is set elsewhere.Emotion's docs are not clear how to handle styling of this nature, at least I have not seen it, so I was hoping that a common solution could be found.
For the gutter
prop, it is trivial:
const GridCol = props => styled('div')`
display: block;
box-sizing: border-box;
flex: 1 0 0;
min-width: 0;
padding: ${props.gutter ? `0 10px` : '0'};
`
For the size
prop, it becomes more complicated, I'd like the resultant CSS to look something like this:
const GridCol = props => styled('div')`
display: block;
box-sizing: border-box;
flex: 1 0 0;
min-width: 0;
padding: ${props.gutter ? `0 10px` : '0'};
/* styles here if `size` is a string */
width: 10%;
/* styles here if `size` is an object */
@media screen and (min-width: 500px) {
width: 20%;
}
@media screen and (min-width: 800px) {
width: 30%;
}
@media screen and (min-width: 1100px) {
width: 40%;
}
`
The width
values will be determined by the prop's key, which corresponds to a value in a breakpoints
object, this part is not trivial, but I don't know how to dynamically generate the css needed.
I'm sure there's more info that I could add, I have made some attempts but none of them are working at the moment. My feeling is that I should create a stateless functional component that generates the css for each condition, then joins the CSS at the end..
If you are looking for a way to implement conditional styles with Emotion's css
prop, you can do something like this:
import { css } from '@emotion/core';
const styles = ({ isSelected }) => css`
border: solid 1px black;
border-radius: 10px;
padding: 16px;
cursor: pointer;
${isSelected === true &&
`
background-color: #413F42;
color: white;
`}
`;
const ConditionalComponent = () => {
const [isSelected, setIsSelected] = useState(false);
return (
<div css={styles({ isSelected })} onClick={() => setIsSelected(!isSelected)}>
Click here to change styles.
</div>
);
};
More details on this here: https://seanconnolly.dev/emotion-conditionals
This is a great question. First, avoid this pattern.
const GridCol = props => styled('div')`
display: block;
box-sizing: border-box;
flex: 1 0 0;
min-width: 0;
padding: ${props.gutter ? `0 10px` : '0'};
`
In this example, a new styled component is created on every render which is terrible for performance.
Any expression, or interpolation, can be a function. This function will receive 2 arguments: props
and context
const GridCol = styled('div')`
display: block;
box-sizing: border-box;
flex: 1 0 0;
min-width: 0;
padding: ${props => props.gutter ? `0 10px` : '0'};
`
As for the size
prop in your example, I would use the following pattern.
import { css } from 'emotion'
const sizePartial = (props) => typeof props.size === 'string' ?
css`width: 10%;` :
css`
@media screen and (min-width: 500px) {
width: 20%;
}
@media screen and (min-width: 800px) {
width: 30%;
}
@media screen and (min-width: 1100px) {
width: 40%;
}
`
You can then use the partial just like any other function that occurs in an expression.
const GridCol = styled('div')`
display: block;
box-sizing: border-box;
flex: 1 0 0;
min-width: 0;
padding: ${props => props.gutter ? `0 10px` : '0'};
${sizePartial};
`
This is an extremely powerful pattern that can be used to compose reusable dynamic styles across your project.
If you are interested in other libraries that leverage this pattern check out https://github.com/emotion-js/facepaint and https://github.com/jxnblk/styled-system
Emotion has a helper called cx that provides functionality similar to the popular classnames library. You can use this to write conditionals more easily:
import { cx, css } from '@emotion/css'
const cls1 = css`
font-size: 20px;
background: green;
`
const foo = true
const bar = false
const SomeComponentWithProp = ({ foo }) => (
<div
className={cx(
{ [cls1]: foo === 'bar' },
)}
/>
);
(Adapted from the linked docs)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With