Emotionを使いこなす

# はじめに

先日、EmotoinをNext.jsに導入する記事を書きました。
[Next.js 10 + TypeScriptでEmotionを使う](https://qiita.com/282Haniwa/items/243f00c39ee7c992d7f7)

今度は、Emotionをどのように使えるのかを記事にしていきたいと思います。

Emotionを使うと何が嬉しいのかはこちらで記事にしているので、是非こちらも読んでみてください！
[Emotionはいいぞ](https://qiita.com/282Haniwa/items/93edc81a884add528593)


# Emotionを使う

まず、Emotionの基本的な使い方について、解説していきます。

Emotionでは以下のようにCSSを記述することができます。

```ts
import React from 'react'
import { css } from '@emotion/react'

// パターン１
const hello = css`
  color: red;
`

type Props = {
  name: string
}

export const Hello: React.FC<Props> = (props) => {
  const { name } = props

  return (
    <div>
      <h1 css={hello}>Hello {name}!</h1>
      {/* パターン２ */}
      <h1
        css={css`
          color: blue;
        `}
      >
        Hello {name}!
      </h1>
    </div>
  )
}
```

helloという変数にバインドしてcssプロパティに指定している方法（パターン１）と、直接cssプロパティに渡している方法（パターン２）です。

この時のhelloにバインドされている値は、以下のようになっています。

```ts
const hello = {
  map: "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,... */"
  name: "1kj5occ-hello"
  styles: "color:red;label:hello;"
  toString: ƒ _EMOTION_STRINGIFIED_CSS_ERROR__()
}
```

このオブジェクトをcssプロパティが解釈してcssを生成し、classNameにセットしているという流れです。

`label`という見慣れない項目が`styles`にありますが、これはEmotion独自の機能で、このcssの生成するクラス名に`label`の文字列が結合されて、デバッグしやすくなるというものです。

特に指定しなければ、バインドした変数の名前が設定されます。
パターン２の方では、コンポーネントの名前が設定されます。

> Generated class names include the name of the variable or component they were defined in.

https://emotion.sh/docs/@emotion/babel-plugin#labelformat

とあるように、Babelによって変数の名前が設定されるようになっているので、デバッグがしやすいように直接cssプロパティに指定するのではなく、一度変数にバインドするようにしましょう。
`label`に独自の名前を設定して、クラス名に含めることも可能なので、デバッグしやすいように使ってください。

cssの宣言は

```ts
const classes = {
  // css-8bac6k-hoge
  hoge: css``,

  // css-1m4255s-test-fuga
  fuga: css`
    label: test;
  `
}
```

のようにもできるので、このようにしておくと便利でしょう。

Babelの設定でファイル名やディレクトリ名を含むようにすることも可能なので、cssの場所がわかりにくいプロジェクトだと設定をしておくとデバッグがしやすくなるかもしれません。
必要に応じて設定してください。


# CSSをマージする

マージと書きましたが、Compositionです。
他で定義したcssのオブジェクトをcssテンプレート文字列の中に埋め込むことができます。

```ts
const borderBase = css`
  border: 1px solid gray;
  border-radius: 5px;
`

const iconButton = css`
  ${borderBase}
  border-radius: 50%;
`
```

また、cssに配列で指定することで、優先度を指定してクラスを適応することができます。

```
<button css={[borderBase, iconButton]}>?</button>
```

細かくは解説しないですが、これによって解決されるCSSの問題点もあります。
気になる人は[公式の解説](https://emotion.sh/docs/composition)がわかりやすいので、是非読んでみてください。

# 擬似要素・擬似クラスやメディアクエリを使う

メディアクエリや擬似要素・擬似クラスなども記述することができます。
sassやscssのように、`&`をそのクラス名として使うことができるので、以下のように記述できます。

```
const hoge = css`
  &:hover {
    color: red;
  }
  @media (min-width: 420px) {
    &:hover {
      color: blue;
    }
  }
`

<div css={hoge}>test</div>
```

参考:
https://emotion.sh/docs/nested
https://emotion.sh/docs/media-queries


# グローバルなスタイルを設定する

グローバルなスタイルが必要な場合もあるでしょう。
その場合は、以下のように設定します。

```
import { Global, css } from '@emotion/react'

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

しかし、これを多用してクラス名を宣言するとEmotionの全てのクラス名が一意であるという利点が失われてしまうので気をつけてください。
タグのデフォルトのCSSを打ち消す場合などには便利だと思います。

参考: https://emotion.sh/docs/globals


# Themeを使う

ThemeをProviderに設定して使うことができます。

```ts
import { ThemeProvider } from '@emotion/react'

const theme = {
  colors: {
    primary: 'blue'
  }
}

render(
  <ThemeProvider theme={theme}>
    <div
      css={(theme) =>
        css`
          color: ${theme.colors.primary};
        `
      }
    >
      hoge
    </div>
  </ThemeProvider>
)
```

themeなんてコンスタントな変数で宣言してimportすればいいじゃんと思う人もいるかもしれませんが、ライトテーマとダークテーマを切り替えたい場合などにProviderに設定しておくことで切り替えやすく、便利にthemeを扱えます。

TypeScriptではThemeの型はデフォルトではからのオブジェクト(`{}`)になっているので、型を宣言してやらないといけません。

```ts:@types/theme.d.ts
import '@emotion/react'

declare module '@emotion/react' {
  export interface Theme {
    color: string
    secondaryColor: string
  }
}
```

このようにTheme型を宣言して、読み込ませましょう。

参考：
https://emotion.sh/docs/theming
https://emotion.sh/docs/emotion-11#theme-type


# Object Styleを使う

基本的には`css`をタグ付きテンプレートリテラルとして使うのが使いやすいと思いますが、オブジェクト形式でCSSを記述することもできます。

Object形式の方がスクリプトから弄りやすいなどのメリットもあるので、必要に応じて使い分けてみてください。
テンプレート文字列に大量の色を埋め込むよりかは、オブジェクト形式で記述した方が可読性が上がる場合などがあるかもしれません。

```ts
const hoge = css({
  backgroundColor: color
})
```

参考： https://emotion.sh/docs/object-styles


# 忘れがちなこと

Emotionを使っていると、コンポーネントの外部から設定した`css`が効かなくて悩むことがあります。（私もこれで小一時間悩みました）

コンポーネントを実装した場合で、`css`によるスタイルの拡張を許したい場合は、`className`をpropsで受け取って、対象のElementに渡すようにしましょう。

`css`プロパティは`css`に渡されたオブジェクトを解釈して`className`にクラス名を渡すというような挙動をするので、コンポーネント側で`className`をちゃんと設定しないと機能してくれません。

意外と忘れがちなので、特に変なことをしていないのに`css`が効かないという時があれば、これを確認してみましょう。


# 参考
- https://emotion.sh/docs/introduction

 
# まとめ

最後まで読んでいただきありがとうございます。
どうだったでしょうか？

個人的に、これだけ使えればEmotionを使いこなせていると言えるんじゃないかという基準でまとめてみました。
他にも解説しきれていない機能などもありますので、是非公式ドキュメントの方も読んでEmotionを使いこなして行ってください！