Emotion

Emotion is a tool for doing CSS-in-JS • https://emotion.sh

Emotion is our styling tool of choice for webapps. Use Emotion if you need CSS styles that aren’t in or appropriate to put in Fleet.

CSS-in-JS is attractive for us because it lets us write CSS that is co-located with our React components. Emotion styles are scoped to the component where they’re used, so it’s very easy to reason about the effects changing the styles would cause.

We picked Emotion over Next.js’s default styled-jsx because the latter is still reliant on user-generated class names to associate styles with components, which means that you a) have to come up with them; b) risk conflicting with other class names; and c) don’t get automatic dead code detection the way you do when you stop referencing an Emotion style.

Emotion Toolchain

We have two helpers that make using Emotion better and safer.

The jest-emotion package interprets Emotion class names in Jest snapshots (such as those generated by StoryShots). It unrolls the CSS so that it gets diffed in the snapshot. Without it, CSS changes only appear in the snapshots as generated class name changes.

We use the eslint-plugin-emotion package with ESLint to enforce setting the proper /** @jsx jsx */ pragma and @emotion/core import statements when the Emotion 10 css prop is used (see below).

Emotion does have two Babel plugins, but we’ve decided against using them. The babel-emotion plugin generates slightly nicer class names and can make source maps. The @emotion/babel-preset-css-prop preset includes babel-emotion and also automatically uses the @emotion/core jsx factory for all JSX so you can use css props.

We like to avoid Babel plugins because they introduce magic that can be hard to track down. We’re much happier with the solution of having eslint-plugin-emotion add /** @jsx */ pragmas for us, since it’s nearly as automatic but significantly more transparent as to what’s happening.

Emotion 10 Migration

Emotion released a new version, 10, that makes use of a JSX factory to support a css prop on React components. When styles are applied via the css prop, no extra work needs to be done to automatically inject <style> elements for server-side rendering.

Currently, all of our webapps use the emotion package, which uses the pre-10 API. With this API, the css() method returns a string for the style’s auto-generated class name. We use these values in className attributes in the JSX, often in template literals when they are composed with other class names. These apps also have code in _document.tsx and _app.tsx to handle the server-side rendering (and to set up a CacheProvider for legacy compatibility).

react-fleet has been updated to use css() from @emotion/core, which generates a style object that needs to get applied via the css prop. It is therefore ready for when we start using the version 10 API in our apps.

It would be nice in general to use version 10 because that lets us remove the awkward SSR support. Given the backwards compatibility interface of the emotion package, it’s not worth actively converting old apps. New apps, however, should start from the ground-up using the css prop.

Last updated