BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Emotion 10: CSS-in-JS with Flexible Scoped and Global Styling, and Server-Side Rendering

Emotion 10: CSS-in-JS with Flexible Scoped and Global Styling, and Server-Side Rendering

Bookmarks

The recent major version of Emotion, a CSS-in-JS library, is a massive and long-awaited release with new features, improvements and bug fixes. A number of major features stand out. Components can now be styled with the css property (prop) in a larger set of contexts, with a more natural syntax which allows access to the theme properties. A new Global component enables dynamic global styling. Those changes contribute to enabling zero-configuration server-side rendering.

Emotion 10.0 no longer requires using a Babel plugin to use the css prop in styled components. As a result, developers using the Emotion library may use projects that do not allow custom Babel configurations (Create React App, code playgrounds such as codesandbox.io, and more). It is now possible to set the Emotion’s jsx pragma at the top of the source file that uses the css prop:

/** @jsx jsx */
import { jsx } from '@emotion/core'

Style precedence is now fixed and documented in Emotion 10.0. The precedence rules are chosen so that components with styles defined on their css prop may be customized via the className prop passed from the parent. The documentation summarizes the precedence rules:

  • Class names containing Emotion styles from the className prop override css prop styles.
  • Class names from sources other than Emotion are ignored and appended to the computed Emotion class name.

and gives an illustrative example. The P component thereafter has its default styles overridden in the ArticleText component, and the same occurs with SmallArticleText which is a specialization of ArticleText:

/** @jsx jsx */
import { jsx } from '@emotion/core'

const P = props => (
  <p
    css={{
      margin: 0,
      fontSize: 12,
      lineHeight: '1.5',
      fontFamily: 'sans-serif',
      color: 'black'
    }}
    {...props} // <- props contains the `className` prop
  />
)

const ArticleText = props => (
  <P
    css={{
      fontSize: 14,
      fontFamily: 'Georgia, serif',
      color: 'darkgray'
    }}
    {...props} // <- props contains the `className` prop
  />
)

const SmallArticleText = props => (
  <ArticleText
    css={{
      fontSize: 10
    }}
    {...props} // <- props contains the `className` prop
  />
)

As expected, and in line with the CSS specification’s Order of Appearance rule, property values defined last (+) override those defined first (-):

.css-result {
+ margin: 0;
- font-size: 12px;
+ line-height: 1.5;
- font-family: 'sans-serif';
- color: black;
- font-size: 14px,
+ font-family: Georgia, serif,
+ color: darkgray;
+ font-size: 10px;
}

The CSS for the SmallArticleText will include the styling received from P (margin: 0) and ArticleText (color: 'darkgray'). However, the font size (font-size: 10px) defined for SmallArticleText will override the font size defined higher in the component hierarchy. Similarly, the color style defined in ArticleText overrides the one defined in its parent P (color: 'black').

As stated by the second precedency rules, class names from sources other than Emotion do not impact the precedence resolution.

Emotion 10.0 now accepts components that simultaneously have a css property and other properties passed through the object spread syntax. The css prop also accepts a functional syntax which exposes the theme properties. The release note gives the following example:

/** @jsx jsx */
import { jsx } from "@emotion/core";
import { ThemeProvider } from "emotion-theming";
import { render } from "react-dom";

function Header(props) {
  return (
    <h1
      css={theme => ({
        fontSize: 48,
        fontWeight: 600,
        color: theme.colors.header
      })}
      {...props}
    />
  );
}

function BodyText(props) {
  return (
    <p
      css={theme => ({
        color: theme.colors.primary,
        fontFamily: "sans-serif",
        fontSize: 18,
        "&:hover": {
          color: theme.colors.hover
        }
      })}
      {...props}
    />
  );
}

function App() {
  return (
    <ThemeProvider
      theme={{
        colors: {
          primary: "hotpink",
          hover: "crimson",
          header: "dimgray"
        }
      }}
    >
      <div>
        <Header>Header Title</Header>
        <BodyText>Hello Emotion 10!!!</BodyText>
      </div>
    </ThemeProvider>
  );
}

render(<App />, document.getElementById("root"));

The example showcases the css prop functional syntax (css={theme => ({...), and the co-location of the object spread syntax with the css prop (<p css={theme => (...) {...props} />).

The new Global component enables dynamic update or removal of global styling. The corresponding feature documentation illustrates:

import { Global, css } from '@emotion/core'

render(
  <div>
    <Global
      styles={css`
        * {
          color: hotpink !important;
        }
      `}
    />
    <Global
      styles={{
        '.some-class': {
          fontSize: 50,
          textAlign: 'center'
        }
      }}
    />
    <div className="some-class">
      Everything is hotpink now!
    </div>
  </div>
)

The displayed div will show a centered pink text. Global styles are updated/removed when the styles change or when the Global component unmounts.

Zero-configuration server-side rendering is enabled by the previously described changes. The release note states:

If you’re using the new css prop or styled, you don’t need to do anything other than calling React’s renderToString (or renderToNodeStream). This is especially exciting for component libraries because consumers don’t need to do anything special to use a component.
You can now publish a React component to NPM with styles, and it will just work with server-side rendering without requiring consumers to do anything.

Emotion 10.0 includes many more changes, improvements, and bug fixes. A migration guide aims at facilitating an incremental upgrade to the new APIs. The migration guide is supported by an ESLint plugin which automates part of the upgrades.

CSS-in-JS refers to a pattern where CSS is composed using JavaScript instead of defined in external CSS files. Emotion is a runtime CSS-in-JS library which allows definition of component style with an object or CSS-like string style. Runtime CSS-in-JS libraries, such as Emotion or Styled-components, dynamically modify styles at runtime, for instance by injecting style tags into the document. Zero-runtime CSS-in-JS libraries, such as Linaria, or Astroturf extract all the CSS at build time.

Emotion is an open source project available under the MIT license. Contributions are welcome via the Emotion GitHub project and should follow the Emotion code of conduct and contributions guidelines.

Rate this Article

Adoption
Style

BT