(自称) 日本一わかりやすいemotionのドキュメント

※ あくまで自称です。笑
※ 今後もどんどん拡張していく予定です。
※ emotion/styled に関しては割愛する予定です。

## :question: Emotionとは？
フレームワークにとらわれず使うことができるCSS in JSのフレームワーク。
(`styled-components`と比較されがちだが、emotionの方が後発であるため`styled-components`ができることは基本できます。)


## :gear: 初期設定
利用したいEmotionのパッケージをインストールする

### Emotion関連のパッケージ

* [@emotion/css](https://emotion.sh/docs/@emotion/css)
  * フレームワークにとらわれず、Emotionを使用するためのパッケージ。
* [@emotion/react](https://emotion.sh/docs/@emotion/react)
  * React用のEmotionパッケージ。
* [@emotion/styled](https://emotion.sh/docs/@emotion/styled)
  * [styled-component](https://styled-components.com/) っぽい記法で書くことができるEmotionパッケージ。(筆者は`styled-component`から乗り換える人用のパッケージだと思っています)
* @emotion/core
  * `@emotion/core` はEmotion 11で `@emotion/react` に改名したため、忘れ去ってください。
  *  参考：https://emotion.sh/docs/emotion-11#package-renaming
* [@emotion/babel-preset-css-prop](https://emotion.sh/docs/@emotion/babel-preset-css-prop)
  *  `css props`を使用する場合、本来はすべてのファイルに`/** @jsx jsx */`を書く必要がある。
  *  この`JSX Pragma`の問題を解決するためのプラグインが`@emotion/babel-preset-css-prop`。
* [@emotion/babel-plugin](https://emotion.sh/docs/@emotion/babel-plugin)
  * Emotionのコンパイルを最適化するためのプラグイン。
  * version 8 以上は不要
* [@emotion/server](https://emotion.sh/docs/@emotion/server)
  * サーバー側レンダリングするAPI


## :pencil2:  基本文法
基本的には2種類の書き方がある。
(厳密には3種類？`@emotion/styled`をインストールすれば`styled-components`の書き方もできます！！)

* オブジェクトスタイル
  * :thumbsup_tone2: 厳密な型定義がされるため予測変換しやすい
  * :thumbsdown_tone2: CSSプロパティがキャメルケースになる

* ストリングスタイル
  * :thumbsup_tone2: そのままのcssを文字列に展開するだけ
  * :thumbsdown_tone2: 型定義がないため、デフォだと予測変換がないしエラー検知しづらい
     * vscodeであれば、[vscode-styled-components](https://marketplace.visualstudio.com/items?itemName=styled-components.vscode-styled-components)を入れると予測変換が使える

```jsx
// オブジェクトスタイル
const objectStyle = css({
  maxWidth: '400px',
  margin: '0 auto',
  padding: '10px'
});

// ストリングスタイル
const stringStyle = css`
  max-width: 400px;
  margin: 0 auto;
  padding: 10px
`

// css propは css({}) もしくは css`` を代入するだけ
render(
  <div css={stringStyle}>
    {/* ... */}
  </div>
)
```

### 擬似要素やメディアクエリー
両方とも、scssの記述方法で入れ子にすればokそうです！！

#### ストリングスタイル
```tsx
const styles = {
  main: css`
    padding: 12px;

    &:hover {
      opacity: 0.5;
    }
  `
}

```


#### オブジェクトスタイル
```tsx
const styles = {
  main: css({
    padding: '12px'
    
    '&:hover': {
      opacity: 0.5,
    },
  })  
}
```

### Propsで渡す
`css prop`に記載されたスタイルが評価され、計算されたクラス名が`className`に渡されます。
そのため、cssを親から受け取りたいComponentはpropsに`css(SerializedStyles型)`ではなく`className(string型)`を受け取れるようにすればよいです。(もちろんcssとして受け取ることもできます。)

とても賢いが正直わかりづらい。

```js
const Button = ({className, children}) => (
  <button className={className}>{children}</button>
)

const buttonWrapperStyle = css`
  background-color: #ddd;
`

// cssで渡しても、classNameに変換される
const ButtonWrapper =  () => (
  <Button css={buttonWrapperStyle}>ボタン</Button>
)
```


### 結合する
EmotionにおけるCSSの結合は、Specificityも解決します。

https://emotion.sh/docs/composition

基本、配列で渡せば結合されます。

```jsx
const marginBottom = css`
  margin-bottom: 16px
`

const style = css`
  padding: 16px;
`

render (
  <div css={[marginBottom, style]}>{/* */}</div> 
)
```


#### ストリングスタイル
そのまま変数代入展開することができる。

```js
const marginBottom = css`
  margin-bottom: 16px
`

const style = css`
  ${marginBottom}
  padding: 16px;
`
```

#### オブジェクトスタイル
スプレッド演算子で展開することができる。

```js
const marginBottom = css({
  marginBottom: '16px'
})

const style = css({
  ...marginBottom,
  padding: '16px'
})
```

### 入れ子

#### ストリングスタイル

###### 子要素に対してスタイルを当てる

```jsx
const listStyle = css`
  li {
    //...
  }
`

return (
  <ul css={listStyle}>
    <li css={item}>{/* */}</li>
  </ul>
);
```

###### 親のタグを指定する
親のタグに囲まれているときのみスタイルを当てることもできる

```jsx
const item = css`
  ul & {
    //...
  }
`

return (
  <>
    <ul>
      <li css={item}>{/* あたる */}</li>
    </ul>
    <div>
      <span css={item}>{/* あたらない */}</span>
    </div>
  </>
);
```

## :computer: 設計

### Media Query
※ マサカリ大歓迎です

```jsx
type breakpointsType = {
  id: 'tablet' | 'pc';
  breakpoint: number;
}[];

const breakpoints: breakpointsType = [
  {
    id: 'tablet',
    breakpoint: 960,
  },
  {
    id: 'pc',
    breakpoint: 560,
  },
];

// min-widthのみしか設定できてないからもう少し工夫は必要かも？
export const MediaQuery = breakpoints.reduce((acc, { id, breakpoint }) => {
  return {
    ...acc,
    [id]: `@media (min-width: ${breakpoint}px)`,
  };
}, {});
```

```jsx
// inline style
const style = css`
  background-color: '#fff';
  ${MediaQuery['pc']} {
    color: gray;
  }
`

// object style
const style = css({
  backgroundColor: '#fff',
  [MediaQuery['pc']]: {
    padding: 20,
  },
});
```