Skip to content

Latest commit

 

History

History
279 lines (218 loc) · 7.72 KB

css-prop.mdx

File metadata and controls

279 lines (218 loc) · 7.72 KB
title
The css Prop

The primary way to style elements with emotion is the css prop. It provides a concise and flexible API to style your components.

Get Started

There are 2 ways to get started with the css prop.

Both methods result in the same compiled code. After adding the preset or setting the pragma as a comment, compiled jsx code will use emotion's jsx function instead of React.createElement.

Input Output
Before <img src="avatar.png" /> React.createElement('img', { src: 'avatar.png' })
After <img src="avatar.png" /> jsx('img', { src: 'avatar.png' })

Babel Preset

This method will not work with Create React App or other projects that do not allow custom Babel configurations. Use the JSX Pragma method instead.

.babelrc

{
  "presets": ["@emotion/babel-preset-css-prop"]
}

Full @emotion/babel-preset-css-prop documentation

If you are using the compatible React version (>=16.14.0) then you can opt into using the new JSX runtimes by using such configuration:

.babelrc

{
  "presets": [
    [
      "@babel/preset-react",
      { "runtime": "automatic", "importSource": "@emotion/react" }
    ]
  ],
  "plugins": ["@emotion/babel-plugin"]
}

In case you want to use the new JSX runtimes with Next.js and you are using their next/babel preset then the configuration should look like this:

.babelrc

{
  "presets": [
    [
      "next/babel",
      {
        "preset-react": {
          "runtime": "automatic",
          "importSource": "@emotion/react"
        }
      }
    ]
  ],
  "plugins": ["@emotion/babel-plugin"]
}

JSX Pragma

Set the jsx pragma at the top of your source file that uses the css prop. This option works best for testing out the css prop feature or in projects where the babel configuration is not configurable (create-react-app, codesandbox, etc.).

/** @jsx jsx */

Similar to a comment containing linter configuration, this configures the jsx babel plugin to use the jsx function instead of React.createElement.

If you are using a zero-config tool with automatic detection of which runtime (classic vs. automatic) should be used and you are already using a React version that has the new JSX runtimes (hence runtime: 'automatic' being configured automatically for you) such as Create React App 4 then /** @jsx jsx */ pragma might not work and you should use /** @jsxImportSource @emotion/react */ instead.

JSX Pragma Babel Documentation

Import the jsx function from @emotion/react

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

Note that excluding this will cause your css to render as [Object Object].

Use the css prop

Any component or element that accepts a className prop can also use the css prop. The styles supplied to the css prop are evaluated and the computed class name is applied to the className prop.

Object Styles

The css prop accepts object styles directly and does not require an additional import.

// @live
render(
  <div
    css={{
      backgroundColor: 'hotpink',
      '&:hover': {
        color: 'lightgreen'
      }
    }}
  >
    This has a hotpink background.
  </div>
)

Object Style Documentation.

String Styles

To pass string styles, you must use css which is exported by @emotion/react, it can be used as a tagged template literal like below.

// @live
import { css } from '@emotion/react'

const color = 'darkgreen'

render(
  <div
    css={css`
      background-color: hotpink;
      &:hover {
        color: ${color};
      }
    `}
  >
    This has a hotpink background.
  </div>
)

Note:

css from @emotion/react does not return the computed class name string. The function returns an object containing the computed name and flattened styles. The returned object is understood by emotion at a low level and can be composed with other emotion based styles inside of the css prop, other css calls, or the styled API.

You can also pass in your css as variables, which allows for composition (read more about this here).

Style Precedence

  • 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.

The precedence order may seem counter-intuitive, but it allows components with styles defined on the css prop to be customized via the className prop passed from the parent.

The P component in this example has its default styles overridden in the ArticleText component.

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
  />
)

The ArticleText component can be customized and the styles composed with its default styles. The result is passed P and the process repeats.

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
  />
)

The styles are concatenated together and inserted via insertRule.

  1. P component
.css-1 {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  font-family: sans-serif;
  color: black;
}
  1. ArticleText component
.css-2 {
  font-size: 14px;
  font-family: Georgia, serif;
  color: darkgray;
}
  1. SmallArticleText component
.css-3 {
  font-size: 10px;
}
  1. Result
.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;
}

Relying on the css spec's "Order of Appearance" rule, property values defined later (green) override those before it (red).

Gotchas

  • If you include the plugin @babel/plugin-transform-react-inline-elements in your .babelrc your styles will not be applied. The plugin is not compatible with the css prop.
  • When using React.cloneElement you can't easily add a css prop if the cloned element doesn't already have it. Otherwise React.cloneElement works with jsx-created elements.