dribbblefacebookgithubgooglepluslinkedinrsstwitter

JavaScript Styling with Feeling

Posted on by Devin Clark

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.