|
| 1 | +--- |
| 2 | +sidebar_label: Emotion |
| 3 | +--- |
| 4 | + |
| 5 | +# Styling apps with Emotion |
| 6 | + |
| 7 | +```mdx-code-block |
| 8 | +import { EuiCode } from '@elastic/eui'; |
| 9 | +``` |
| 10 | + |
| 11 | +[Emotion](https://emotion.sh/) is the underlying CSS-in-JS library used by EUI. |
| 12 | +It helps us manage CSS styles at scale injecting precisely what's needed |
| 13 | +to render visible components, _fast_. |
| 14 | +For best compatibility, we highly recommend all apps using EUI to implement |
| 15 | +Emotion as their primary styling method. |
| 16 | + |
| 17 | +## What's Emotion and CSS-in-JS? |
| 18 | + |
| 19 | +CSS-in-JS is a concept of writing styles directly in JS/TS files. |
| 20 | +While this may sound strange at first, it comes with many advantages compared |
| 21 | +to traditional CSS or SASS/LESS. |
| 22 | + |
| 23 | +It brings the plain old CSS syntax you already know to the JavaScript world, |
| 24 | +so that your style definitions reference React components directly |
| 25 | +and predictably. No more class name guesswork, selector specificity issues |
| 26 | +or loading unnecessarily large stylesheets on each page load. |
| 27 | + |
| 28 | +Emotion can compose and optimize styles, generate source maps and labels |
| 29 | +for easy debugging, and includes powerful testing utilities enabling assertions |
| 30 | +on real stylesheets and CSS properties. |
| 31 | + |
| 32 | +<Demo isSourceOpen> |
| 33 | + ```tsx |
| 34 | + import { css } from '@emotion/react'; |
| 35 | + |
| 36 | + // Declaring styles in a template literal is just like writing plain CSS |
| 37 | + const rainbowStyles = css` |
| 38 | + padding: .25rem; |
| 39 | + border-radius: .5rem; |
| 40 | + background: linear-gradient(90deg in hsl longer hue, red, red); |
| 41 | + /* Edit me and try this out! */ |
| 42 | + /* filter: invert() */ |
| 43 | + `; |
| 44 | + |
| 45 | + export default <div css={rainbowStyles} />; |
| 46 | + ``` |
| 47 | +</Demo> |
| 48 | + |
| 49 | +We highly encourage reading [Emotion docs](https://emotion.sh/docs/introduction) |
| 50 | +to better understand its underlying concepts. |
| 51 | + |
| 52 | +## Writing styles |
| 53 | + |
| 54 | +We recommend writing styles using the template literal notation |
| 55 | +(e.g., <EuiCode language="tsx">css\`color: red\`</EuiCode>), also called |
| 56 | +_string styles_. It makes writing styles very similar to the native CSS styling |
| 57 | +experience and allows seamless usage of pseudo classes like `:hover` |
| 58 | +or `::before`. |
| 59 | + |
| 60 | +You can pass any regular CSS styles to the `css` template literal. The styles |
| 61 | +are processed by Emotion using [Stylis](https://github.com/thysultan/stylis) |
| 62 | +which can be configured to run plugins like vendor prefixing. By default, |
| 63 | +EUI applies CSS property prefixes targeting the latest evergreen browsers, |
| 64 | +following our [supported browsers matrix](https://www.elastic.co/support/matrix#matrix_browsers). |
| 65 | + |
| 66 | +## Frequently Asked Questions |
| 67 | + |
| 68 | +### What's the difference between the `css` imported from `@emotion/react` and `@emotion/css`? |
| 69 | + |
| 70 | +[`@emotion/css`](https://emotion.sh/docs/@emotion/css) is the vanilla JS |
| 71 | +version of Emotion. It does not require any babel setup. It generates a `css-` |
| 72 | +prefixed className with the attached styles, which can be applied directly |
| 73 | +to vanilla JS DOM nodes or passed to a React `className={}` prop, e.g. |
| 74 | + |
| 75 | +```tsx |
| 76 | +import { css } from '@emotion/css'; |
| 77 | +const styles = css` |
| 78 | + color: red; |
| 79 | +`; |
| 80 | +<EuiComponent className={styles} /> |
| 81 | +``` |
| 82 | + |
| 83 | +[`@emotion/react`](https://emotion.sh/docs/@emotion/react) is mostly |
| 84 | +syntactical sugar on top of the vanilla JS library. It allows usage |
| 85 | +of the `css` prop, which handles setting the generated CSS onto the underlying |
| 86 | +component's `className`, and automatically concatenates any other `css` styles |
| 87 | +passed onto the component together into a single ruleset for you. Example: |
| 88 | + |
| 89 | +```tsx |
| 90 | +import { css } from '@emotion/react'; |
| 91 | +const styles = css` |
| 92 | + color: red; |
| 93 | +`; |
| 94 | +<EuiComponent css={styles} /> |
| 95 | +``` |
| 96 | + |
| 97 | +Please note that `@emotion/react` usage requires a |
| 98 | +[babel preset](https://www.npmjs.com/package/@emotion/babel-preset-css-prop) |
| 99 | +setup, and that Kibana plugins already set up to use `styled-components` cannot |
| 100 | +use both `styled-components` and `@emotion/react` at the same time. |
| 101 | + |
| 102 | +#### When should I use one vs the other? |
| 103 | + |
| 104 | +In general, when working directly in React/JSX, we recommend using |
| 105 | +`@emotion/react` for the nicer `css` prop syntactical sugar. |
| 106 | + |
| 107 | +For consistency, we recommend using `@emotion/css` only when necessary. |
| 108 | +Examples of necessary (but hopefully rare) use-cases: |
| 109 | +- When styling raw JS DOM nodes |
| 110 | +- If your Kibana plugin is set up with `styled-components`, it will not support `@emotion/react` and you must use `@emotion/css` instead |
| 111 | +- If, for some reason, you absolutely do not want your styles to be automatically concatenated with other Emotion styles into a single selector, and want it to remain its own separate className/selector |
| 112 | + |
| 113 | +### Can I use both `styled-components` and `@emotion/react` at the same time? |
| 114 | + |
| 115 | +No. If your Kibana plugin uses babel to compile `styled-components` (please see this file/regex to check if your plugin is listed: https://github.com/elastic/kibana/blob/main/packages/kbn-babel-preset/styled_components_files.js), you cannot use `@emotion/react`. |
| 116 | + |
| 117 | +`styled-components` and `@emotion/react` are incompatible and Kibana can't use the babel plugins for both on the same code. While Kibana will not actively throw if you try to do so, and may actually attempt to (**very** inconsistently) render some Emotion styles, this is actually a bug on Emotion's end - they drop some validation in `NODE_ENV=production` which allows their styles to sometimes be applied even when they're not supposed to. You will still see missing CSS, however, and you should be careful not to use both. The giveaway is if you see `Styled` in the classNames of your component DOM. |
| 118 | + |
| 119 | +If you still wish to use Emotion while your plugin is setup for `styled-components`, all is not lost - you can use vanilla JS `@emotion/css` passed to the `className` prop in the interim. |
| 120 | + |
| 121 | +#### Should I migrate my plugin away from `styled-components` to Emotion? |
| 122 | + |
| 123 | +Yes, teams should begin migrating. While we don't yet have a set target date for moving entirely away from styled-components in Kibana, we expect to share one soon. |
| 124 | + |
| 125 | +Our goal is to standardize on Emotion as our CSS-in-JS solution in Kibana. This will ensure consistency, easier support, and a unified theming system across the platform. |
| 126 | + |
| 127 | +Here are a few key points to consider: |
| 128 | + |
| 129 | +- The EUI team is more able to help assist with or debug styling issues in Emotion over styled-components |
| 130 | +- EUI exports Emotion style utilities that we do not have available for styled-components |
| 131 | +- EUI has already set up cache providers for Emotion in, which help control where styles render on the page & can determine specificity/order, that we do not have set up for styled-components |
| 132 | + |
| 133 | +As a simple first step in this transition, consider using [@emotion/styled](https://emotion.sh/docs/styled). This allows you to keep the familiar styled syntax while aligning with the Emotion ecosystem |
| 134 | + |
| 135 | +We've also found [this guide](https://simonhartcher.com/migrating-from-styled-components-to-emotion) to be a useful reference for migration. |
| 136 | + |
| 137 | +### Theming and Emotion's `css` prop |
| 138 | + |
| 139 | +Emotion's `css` prop allows consumers to [receive the EUI theme](https://emotion.sh/docs/theming#css-prop) from a callback within the prop: |
| 140 | + |
| 141 | +```tsx |
| 142 | +<SomeComponent |
| 143 | + css={({ euiTheme }) => ({ |
| 144 | + color: euiTheme.colors.primaryText, |
| 145 | + padding: euiTheme.size.l, |
| 146 | + })} |
| 147 | +/> |
| 148 | +``` |
| 149 | + |
| 150 | +This allows you to skip calling `useEuiTheme()` and is a nice syntactical sugar shortcut. That being said, there **are** caveats to its usage: |
| 151 | + |
| 152 | +#### Typing |
| 153 | + |
| 154 | +By default, Emotion's args for this API are untyped. You must [define a theme](https://emotion.sh/docs/typescript#define-a-theme) in your own `emotion.d.ts` file for the above callback usage to be properly typed. You can simply extend EUI's theme like so: |
| 155 | + |
| 156 | +```ts |
| 157 | +import '@emotion/react'; |
| 158 | +import type { UseEuiTheme } from '@elastic/eui'; |
| 159 | + |
| 160 | +// @see https://emotion.sh/docs/typescript#define-a-theme |
| 161 | +declare module '@emotion/react' { |
| 162 | + export interface Theme extends UseEuiTheme {} |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +You will then need to import this file into your `tsconfig.json`. See the [osquery plugin](https://github.com/elastic/kibana/blob/main/x-pack/plugins/osquery/tsconfig.json#L17-L18) for an example of their tsconfig setup. |
| 167 | + |
| 168 | +#### Performance |
| 169 | + |
| 170 | +Please note that the above callback syntax **does** come with performance implications, so if a high number of renders is an issue with the component you're styling, we recommend either defining your style callback statically outside the render cycle, or using `useCallback` to memoize your style fn. For example: |
| 171 | + |
| 172 | +```tsx |
| 173 | +const YourStyles = ({ euiTheme }: UseEuiTheme) => ({ |
| 174 | + color: euiTheme.colors.primary, |
| 175 | + padding: euiTheme.size.l, |
| 176 | +}); |
| 177 | + |
| 178 | +export const YourComponent = () => ( |
| 179 | + (<div css={YourStyles} /> |
| 180 | +); |
| 181 | +``` |
| 182 | +
|
0 commit comments