JavaScript Styling with Feeling
This is a quick introduction to how I did CSS-in-JS on a recent project using Emotion and Preact.
To start, I need to install emotion and a Babel plugin. The Babel plugin generates source maps and optimizes style output. Emotion highly recommends this for production use.
npm install --save emotion
npm install --save-dev babel-plugin-emotion
Create a .babelrc file (or add the emotion parts to my existing babelrc file). Source maps will add a lot of weight to my bundle, so I am disabling them in production.
{
"env": {
"production": {
"plugins": [["emotion", { "sourceMap": false }]]
},
"development": {
"plugins": [["emotion", { "sourceMap": true }]]
}
}
}
Now, I can start making components. I've made a basic component to define a section with padding on the bottom. I prefer defining the styles as objects instead of template tags. It feels more natural in JavaScript to me. I wrap all the styles in a styles object. The individual items in the style object are wrapped in emotion's css function.
import { css } from 'emotion';
import { h } from 'preact';
import { GRID_UNIT } from '../constants/layout';
const styles = {
container: css({
paddingBottom: GRID_UNIT * 5,
}),
};
export default function Section() {
return <section className={styles.container}>{props.children}</section>;
}
GRID_UNIT
is a constant I have defined with a value of 8. I make all my dimensions, margins, and padding a multiple of this number. Because of this I am able to get a very uniform interface without much effort.
To use the styles, I just pass the style I want to className. If I need to do any kind of composition I can use cx
from emotion and pass an array of styles that are merged together. cx
uses the classnames API.
import { css, cx } from 'emotion';
import { h } from 'preact';
import { GRID_UNIT } from '../constants/layout';
const styles = {
container: css({
fontSize: 20,
paddingBottom: GRID_UNIT * 3,
}),
};
export default function SectionHeading(props) {
return <h2 className={cx(styles.container, props.className)}>{props.children}</h2>;
}
By passing props.className
to cx
I can override the default styles.
const styles = {
specialHeading: css({
fontSize: 22,
color: 'blue',
}),
};
<SectionHeading className={styles.specialHeading}>Very Special Heading</SectionHeading>;
Next, I use injectGlobal
to do a simple CSS reset.
import { injectGlobal } from 'emotion';
injectGlobal({
html: {
boxSizing: 'border-box',
},
'*': {
boxSizing: 'inherit',
margin: 0,
padding: 0,
':before': {
boxSizing: 'inherit',
},
':after': {
boxSizing: 'inherit',
},
},
});
Being able to snapshot test the styles using jest-emotion is a big plus for me too. You can read more about that on the Emotion docs.