Skip to content

Commit

Permalink
Merge branch 'main' into fix/pure-annotations-before-styled-calls
Browse files Browse the repository at this point in the history
# Conflicts:
#	site/package.json
#	yarn.lock
  • Loading branch information
Andarist committed Jul 26, 2022
2 parents 2d062b1 + 1c32b04 commit 87a7a22
Show file tree
Hide file tree
Showing 149 changed files with 5,485 additions and 13,805 deletions.
2 changes: 1 addition & 1 deletion .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"packages": ["packages/*"],
"sandboxes": ["pk1qjqpw67"],
"node": "12"
"node": "16"
}
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space

[*.{js,ts,tsx,json,yml}]
indent_size = 2
7 changes: 5 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ coverage/
node_modules/
stylis.min.js
/demo/dist
/packages/site/build
flow-typed/
flow-typed/

# This is to prevent ESLint parsing errors in the website which is written in
# TypeScript. TODO: Reenable once the codebase is converted to TypeScript.
/site/
2 changes: 1 addition & 1 deletion .github/actions/ci-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'CI setup'
runs:
using: 'composite'
steps:
- name: Set Node.js 16.x
- name: Setup Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: 16.x
Expand Down
11 changes: 7 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ dist/
node_modules/
*.log
.idea
site/build
package-lock.json
.DS_Store
.cache
public/
!playgrounds/cra/public
.env
.vscode
.parcel-cache/
.parcel-cache/
*.orig
*.tsbuildinfo

# Website
site/out
.next
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
12
16
10 changes: 3 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

## Installation

- (If using an M1 Mac) Install `vips` via Homebrew: `brew install vips`.
- This step can be removed from this document if we upgrade to the latest
version of Gatsby, which is compatible with sharp 0.28.0+ which does include
binaries for M1 Macs.
- (If using Windows) Enable Developer Mode in the Windows settings app. On Windows 11, this can be done by searching the start menu for "Developer settings" and then enabling the Developer Mode toggle switch.
- Run `yarn` in the repository's root directory to install everything you need for development.
- Run `yarn build` in the root directory to build the modules.
Expand All @@ -27,9 +23,9 @@

## Documentation Website Development

- Run above installation steps and then
- Run `yarn start:site` to run a development server of gatsby.
- Run `yarn build:site` to create a build of the assets for the documentation website.
- Run above installation steps and then `cd` to the `site` directory.
- Run `yarn dev` to run the Next.js development server.
- Run `yarn build` to create a build of the assets for the documentation website.

## Changesets

Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
![@emotion/styled size](https://img.shields.io/bundlephobia/min/@emotion/styled.svg?label=@emotion/styled%20size)
![@emotion/styled gzip size](https://img.shields.io/bundlephobia/minzip/@emotion/styled.svg?label=@emotion/styled%20gzip%20size)
[![slack](https://img.shields.io/badge/join-slack-green)](https://join.slack.com/t/emotion-slack/shared_invite/zt-rmtwsy74-2uvyFdz5uxa8OiMguJJeuQ)
[![spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/emotion)

Emotion is a performant and flexible CSS-in-JS library. Building on many other CSS-in-JS libraries, it allows you to style apps quickly with string or object styles. It has predictable composition to avoid specificity issues with CSS. With source maps and labels, Emotion has a great developer experience and great performance with heavy caching in production.

Expand Down
12 changes: 6 additions & 6 deletions docs/babel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ title: 'Babel Plugin'

`@emotion/babel-plugin` is highly recommended. All of the options that can be provided to `@emotion/babel-plugin` are documented in [`@emotion/babel-plugin`'s README](https://github.com/emotion-js/emotion/tree/main/packages/babel-plugin).

### Install
## Install

In `emotion` version 8 and above, installation is optional. In older versions, installation is required. See the [installation instructions](/docs/install.mdx).

### Features which are enabled with the babel plugin
## Features which are enabled with the babel plugin

### Minification
#### Minification

Any leading/trailing space between properties in your `css` and `styled` blocks is removed. This can reduce the size of your final bundle.

### Dead Code Elimination
#### Dead Code Elimination

Uglifyjs will use the injected `/*#__PURE__*/` flag comments to mark your `css` and `styled` blocks as candidates for dead code elimination.

### Source Maps
#### Source Maps

When enabled, navigate directly to the style declaration in your javascript file.

### Components as selectors
#### Components as selectors

The ability to refer to another component to apply override styles depending on nesting context. Learn more in the [styled docs](/docs/styled.mdx#targeting-another-emotion-component).
223 changes: 223 additions & 0 deletions docs/best-practices.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
title: 'Best Practices'
---

Emotion is an extremely flexible library, but this can make it intimidating, especially for new users. This guide contains several recommendations for how to use Emotion in your application. Keep in mind, these are only recommendations, not requirements!

## Recommendations

### Use TypeScript and object styles

You don't get Intellisense or type checking when using CSS strings, e.g. `` css`color: blue` ``. You can improve the developer experience and prevent style bugs by using TypeScript and object styles, which enable Intellisense and _some_ static type checking:

```tsx
const myCss = css({
color: 'blue',
grid: 1 // Error: Type 'number' is not assignable to type 'Grid | Grid[] | undefined'
})
```

### Colocate styles with components

With normal CSS, the styles for a component are defined in a separate file. This makes maintenance more difficult because it's harder to tell which components use a given piece of CSS, and you can easily forget to update the relevant CSS after modifying a component. One of the main benefits of Emotion is that you can [colocate](https://kentcdodds.com/blog/colocation) your styles with your components. All this means is that the CSS for a component should be in the same file as the component itself.

### Consider how you will share styles

There are two main approaches for sharing styles across an Emotion application.

To illustrate the two methods, let's imagine we're developing an application that needs to display error messages in many different components. All of the error messages should be red and bold. Some error messages should also use a large font.

#### Method 1: Export CSS objects

The simplest way to share styles is to export CSS from a shared file and then import that CSS in many places:

```tsx
export const errorCss = css({
color: 'red',
fontWeight: 'bold'
})

// Use arrays to compose styles
export const largeErrorCss = css([errorCss, { fontSize: '1.5rem' }])
```

Then, in one of your components:

```tsx
import { errorCss } from '...'

return <p css={errorCss}>Failed to fizzle the frozzle.</p>
```

This method is great when you only want to share CSS between components. A potential drawback of this method is that shared styles are not colocated with the components that use them.

#### Method 2: Share styles via component reuse

This method is slightly more complex but arguably more powerful than the previous method.

```tsx
export function ErrorMessage({ className, children }) {
return (
<p css={{ color: 'red', fontWeight: 'bold' }} className={className}>
{children}
</p>
)
}

// `fontSize: '1.5rem'` is passed down to the ErrorMessage component via the
// className prop, so ErrorMessage must accept a className prop for this to
// work!
export function LargeErrorMessage({ className, children }) {
return (
<ErrorMessage css={{ fontSize: '1.5rem' }} className={className}>
{children}
</ErrorMessage>
)
}
```

Then, in one of your components:

```tsx
import { ErrorMessage } from '...'

return <ErrorMessage>Failed to fizzle the frozzle.</ErrorMessage>
```

Advantages of sharing styles via component reuse include:

- Component reuse allows you to share both logic and styles. You can easily add additional props and functionality to the `ErrorMessage` component with limited refactoring.
- Styles are always colocated with their components.

### Use the `style` prop for dynamic styles

The css prop or `styled` should be used for static styles, while the `style` prop (inline styles) should be used for truly dynamic styles. By dynamic styles, we mean styles that change frequently or are unique to a single element.

Imagine you are displaying a list of user avatars in a forum application. Every avatar shares certain static CSS, like `width: 40px` and `border-radius: 50%`, but the avatar image is set via a `background-style` rule whose value is different for each avatar. If you pass all of this CSS through the CSS prop, you'll end up with a lot of nearly-duplicate CSS in the document. With 3 avatars, Emotion will create something like:

```html
<style>
.css-1udhswa {
border-radius: 50%;
width: 40px;
height: 40px;
background-style: url(https://i.pravatar.cc/150?u=0);
}
.css-1cpwmbr {
border-radius: 50%;
width: 40px;
height: 40px;
background-style: url(https://i.pravatar.cc/150?u=1);
}
.css-am987o {
border-radius: 50%;
width: 40px;
height: 40px;
background-style: url(https://i.pravatar.cc/150?u=2);
}
</style>
```

Now imagine how much CSS there would be if there were 100 avatars on the page!

You should also use the `style` prop if the styles will be updated frequently. If your application lets a user drag and drop an element, you likely have a `transform` property like

```ts
{
transform: `translate(${x}px, ${y}px)`
}
```

This property should go through the `style` prop since `x` and `y` will change rapidly as the element is dragged.

#### Advanced: CSS variables with `style`

CSS variables can be used with the `style` prop to keep the CSS in a single place while "deferring" the actual value of a property. Going back to the avatar example above, you could define the following static CSS using the css prop:

```css
.avatar {
border-radius: 50%;
width: 40px;
height: 40px;
background-style: var(--background-style);
}
```

Then, for each avatar, you render an element which sets the value of the `--background-style` CSS variable:

```tsx
function Avatar({ imageUrl }) {
return <div className="avatar" style={{ '--background-style': imageUrl }} />
}
```

If you're using TypeScript, you'll have to use a type assertion like `style={{ ['--background-style' as any]: imageUrl }}` as explained [here](https://stackoverflow.com/a/52013197/752601).

### If using React, prefer `@emotion/react` or `@emotion/styled` over `@emotion/css`

`@emotion/react` and `@emotion/styled` generally offer a better developer experience over class names (`@emotion/css`) when using React.

### Use the css prop or `@emotion/styled`, but not both

While the css prop and `styled` can peacefully coexist, it's better to pick one approach and use it consistently across your codebase. Whether you choose the css prop or `styled` is a matter of personal preference. (If you're curious, the css prop is more popular among the maintainers of Emotion.)

### Consider defining styles outside your components

For example:

```tsx
import { css } from '@emotion/react'

const cardCss = {
self: css({
backgroundColor: 'white',
border: '1px solid #eee',
borderRadius: '0.5rem',
padding: '1rem'
}),

title: css({
fontSize: '1.25rem'
})
}

export function Card({ title, children }) {
return (
<div css={cardCss.self}>
<h5 css={cardCss.title}>{title}</h5>
{children}
</div>
)
}
```

Benefits of this approach:

- Styles are only serialized once, instead of on every render.
- It's no longer possible to accidentally pass dynamic styles through the css prop.
- The JSX is arguably more readable with the CSS separated out into a different part of the file.

### Define colors and other style constants as JavaScript variables

Don't repeat yourself! If you are using a color, padding, border radius, .etc throughout your application, add it to your theme or define it as a JavaScript constant, like

```ts
export const colors = {
primary: '#0d6efd',
success: '#198754',
danger: '#dc3545'
}
```

### Don't use a theme unless your app supports multiple themes (or will eventually support multiple themes)

Emotion allows you to define a [theme](/docs/theming.mdx) which you can use in style rules across your application. This feature is awesome if your app has multiple themes, like light mode and dark mode.

On the other hand, if your app will only ever have a single theme, it's simpler to define colors and other style variables as JavaScript constants.

## Additional Reading

- [Colocation](https://kentcdodds.com/blog/colocation) by Kent C. Dodds
- [Change how you write your CSS-in-JS for better performance](https://douges.dev/blog/taming-the-beast-that-is-css-in-js) by Michael Dougall
8 changes: 4 additions & 4 deletions docs/cache-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ It can be useful to customize emotion's options - i.e. to add custom Stylis plug

```jsx
// @live
/** @jsx jsx */
import { CacheProvider, jsx, css } from '@emotion/react'
import { CacheProvider, css } from '@emotion/react'
import createCache from '@emotion/cache'
import { prefixer } from 'stylis'

Expand All @@ -17,9 +16,10 @@ const myCache = createCache({
key: 'my-prefix-key',
stylisPlugins: [
customPlugin,
// has to be included manually when customizing `stylisPlugins` if you want to have vendor prefixes added automatically
// has to be included manually when customizing `stylisPlugins` if you want
// to have vendor prefixes added automatically
prefixer
],
]
})

render(
Expand Down

0 comments on commit 87a7a22

Please sign in to comment.