Next.jsでのEmotionの導入と基本形・メディアクエリ・継承の使い方

現在、エビスコムさんの「作って学ぶ Next.js/React Webサイト構築」を参考にポートフォリオを作成しています。

https://qiita.com/tatsuya_yamakawa/items/bc6d1c6002a6e8997636

今回は、サイト制作に使用するスタイリング方法の一つであるEmotionについての覚書です。

## CSS modules から Emotion に移行

本書はスタイリング方法としてCSS modulesを使用しています。
HTML/CSS経験者なら同じように書くことができるので初心者向けといえます。

ですが、ある程度進んだところでファイル数の増大が気になり始めました。
CSS modulesの特徴としてコンポーネントごとにCSSファイルを分割するので、必然的にファイル数が倍になるのです。

作っているサイトデザインもそれほど複雑ではありません。
セレクタも1ファイルで数えられるくらいしかないのにわざわざ分ける必要はあるのかと疑問に思いました。

Emotionでスタイルを定義するには以下のように書きます。

```react:App.jsx
const App = () => {
  return (
    <div css={hello}>こんにちは！</div>
  )
}

export default App

const hello = css`
  font-size: 1.5rem;
  color: red;
`
```

見てわかるようにコンポーネントと同じファイルにまとめてスタイルを書くことができます。

規模によりますが、小規模サイトなら圧倒的にEmotionが楽だと思います。

## Emotion の導入

では、Emotionをインストールしましょう。

```
npm install @emotion/react @emotion/babel-plugin
or
yarn add @emotion/react @emotion/babel-plugin
```

`@emotion/babel-plugin` はEmotionをコンパイルするプラグインです。

### Babel の設定ファイルの作成

`.babelrc` を作成して以下を追加します。

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

JavaScriptで書く場合は、ここまででEmotionの環境構築完了です。

### TypeScript の場合（型定義の追加）

`tsconfig.json` に以下を追加します。

```json:tsconfig.json
"compilerOptions": {
  "types": [
    "@emotion/react/types/css-prop"
  ],
}
```

これがないと、CSS Propにスタイルを渡すときにエラーが出ます。（以下例）

![2023-03-02_15h42_13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183668/217c7da4-da37-9350-5ed2-d9c28873ae3a.jpeg)

## Emotion の命名規則

すでにEmotionを使えるようになりましたが、命名規則についても語っておきます。

私はこちらの記事を参考にクラス名を決めました。

https://liginc.co.jp/web/html-css/css/166585/2

Next.jsでサイト制作をする場合、コンポーネントごとに大体パターンが決まってくるので使うクラス名も限られてきます。

先にルールさえ決めてしまえば迷うこともなくなり制作もスムーズです。

### stack
記事一覧など繰り返し処理を囲むのに使用します。
stackは直訳すると積み重ねるという意味です。

### wrapper / inner
ブロック要素を囲むのにwapper、中の要素にinnerを使用します。

### group
類似するコンポーネントを囲むのに使用します。

### holder
groupと似ていますが、こちらは異なる種類のコンポーネントをグループ化するのに使用します。
例：Contactコンポーネントのフォームとボタンなど

### list / item
全体を覆う要素（ul）にlist、子要素（li）にitemを使用します。

## Emotion の基本的な使い方

では、Emotionの基本的な使い方を見ていきましょう。

1. CSSモジュールをインポート
2. 変数でスタイルを定義
3. CSS Propでスタイルを呼ぶ

の流れで使用します。

```react:App.jsx
import { css } from '@emotion/react'

const App = () => {
  return (
    <>
      <h1 css={h1Style}>これは見出しです！</h1>
      <p css={pStyle}>本文です！</p>
    </>
  )
}

export default App

const h1Style = css`
  font-size: 2rem;
  color: red;
`

const pStyle = css`
  font-size: 1rem;
  line-height: 1.5;
`
```

### メディアクエリの使い方

メディアクエリも変数内で使用できます。
```react
const h1Style = css`
  font-size: 1.5rem;
  color: red;

  // PCの場合
  @media (min-width: 768px) {
    font-size: 2rem
  }
`
```


### CSS を extend する方法

次にスタイルをextend（継承）する方法を見ていきます。

Emotionでは、CSS modulesのcomposeによるextendはできません。

#### 同一ファイルから extend

同ファイル内で継承する場合は、以下のようにします。

```react
const parentStyle = css`
  font-size: 1.5rem;
  font-weight: 400;
  line-height: 1.7;
  color: red;
`

const childStyle = css`
  ${parentStyle} // parentStyleを継承
  color: blue; // colorだけ上書き
`
```

これでカラーだけ青になったchildStyleを作ることができます。

注意として、**親になるスタイルは子より上に書かないとエラー**になります。

#### ベースファイルを作って extend

おすすめの継承方法として、別ファイルにベーススタイルを作成し呼び出すという方法もあります。
似たスタイルをいろんなコンポーネントで継承して使いまわすときに便利です。

stylesフォルダを作り、`base.js`（好きな名前でOK）を作ります。
エクスポートするのを忘れないようにしましょう。

```react:styles/base.js
import { css } from '@emotion/react'

export const baseTextStyle = css`
  font-size: 1.5rem;
  font-weight: 400;
  line-height: 1.7;
  color: red;
`
```

ベーススタイルをインポートすれば、ほかのファイルで継承して使うことができます。

```react:App.jsx
import { css } from '@emotion/react'
import { baseTextStyle } from 'styles/base'

const App = () => {
  return (
    <>
      <h1 css={extendTextStyle}>継承されたスタイルです！</h1>
    </>
  )
}

const extendTextStyle = css`
  ${baseTextStyle}
  color: blue;
`
```

今回紹介した、基本形、メディアクエリ、継承くらいで小規模サイト程度なら困ることはないかと思います。
参考になれば幸いです。