Next.js と TypeScript で、Emotion の Global Styles を理解する

TwitterFacebookHatena

TL;DR

この記事では Next.js と TypeScript を使用して、CSS-in-JS ライブラリである Emotion の Global Styles の実装方法を解説します。Emotion の Global Styles を用いることで、アプリケーション全体に適用したいスタイルを効率的に管理することが可能です。

開発環境 バージョン
Next.js 13.4.3 (Pages Router)
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

Emotion の Global Styles とは?

Emotion は React における CSS-in-JS の解決策の一つで、コンポーネントのスタイルを JavaScript 内で管理できるライブラリです。Emotion の Global Styles 機能を使うと、アプリケーション全体に適用する CSS を定義することが可能になります。これにより、例えば全体のフォント設定や色の基本設定などを一元管理できるわけです。

リセット CSS やフォントフェイスのようなグローバル CSS を挿入したい場合は、Global コンポーネントを使います。グローバルスタイルは、スタイルが変更された時や Global コンポーネントがアンマウントされた時に削除されます。

Emotion の Global スタイルは基本的にインラインスタイルです。そのため、他の CSS ルールより優先されます。もちろん、ネストや変数を使うこともできます。

Emotion の Global スタイルは静的なものだけでなく、動的なスタイルもサポートしています。つまり、プロパティや状態に基づいてスタイルを変更することが可能です。これにより、ダークモードのような異なるテーマを切り替える機能を簡単に実装できます。

また、利点として、Emotion の Global スタイルは、ページの読み込み時に一度だけ挿入されます。これにより、パフォーマンスに悪影響を与えることなく、全体のスタイルを効率的に管理できます。

Emotion – Global Stylesに書かれているコードを見てみましょう。

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/react というライブラリから Globalcss というものを取り出して使います。このライブラリは、ウェブサイト全体に適用されるスタイルを作るのを助けてくれるツールです。

render 関数は、括弧内のすべてをウェブページに表示させます。これは作りたいウェブページのレイアウトや内容を定義する場所ですね。

<Global /> という部分は、その中に書かれたスタイルがウェブサイト全体に適用されるようにするためのものです。

最初の Global コンポーネントでは、styles プロパティに css 関数を使って、.some-class という名前のクラスに対するスタイルを定義しています。このクラスが適用された要素は、「hotpink」色になります。「!important」は、このスタイルルールが他のスタイルルールよりも優先されることを保証します。

次の Global コンポーネントでは、styles プロパティにオブジェクト形式のスタイルを定義しています。このスタイルでは、.some-class クラスを持つ要素のフォントサイズが 50 になり、テキストが中央揃えになります。

最後に <div className="some-class">This is hotpink now!</div> の部分で、実際に .some-class クラスを使う要素を作ります。この div 要素のテキストは、「This is hotpink now!」となります。そして、先ほど定義したスタイルにより、このテキストは hotpink 色になり、フォントサイズが 50 で、中央揃えに表示されます。

まとめると、このコードは hotpink 色で大きな文字を中央に表示するウェブページを作っているんですね。

Next.js で Emotion の Global Styles を実装

次に、Emotion の Global Styles を Next.js のプロジェクトでどのように実装するかを見ていきます。

Pages Router の例として、_app.tsx ファイルに Global Styles を適用してみましょう。

// pages/_app.tsx

import { Global, css } from '@emotion/react'
import type { AppProps } from 'next/app'

type Props = AppProps

const MyApp = ({ Component, pageProps }: Props) => (
  <>
    <Global
      styles={css`
        body {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
        }
      `}
    />
    <Component {...pageProps} />
  </>
)

export default MyApp

このコードでは Emotion の Global コンポーネントと css 関数を使用して、全体に適用する CSS を定義しています。今回は body タグに対してマージンとパディングの初期化、ボックスサイジングの設定、そしてフォントファミリーの設定を行っています。

ただ、reset.css などを適用したい場合は、視認性が悪くなるので Global Styles に書かずに、次のように import して使うほうがよいです。

import '../styles/reset.min.css'
import '../styles/style.scss'
import { Global, css } from '@emotion/react'

ダークモード切り替えを実装する

ダークモードとライトモードを切り替える方法を見てみましょう。これは、ボタンをクリックするとダークモードとライトモードを切り替えるシンプルなコンポーネントです。

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

const DarkModeToggle = () => {
  const [isDarkMode, setIsDarkMode] = useState(false)

  return (
    <div>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>Toggle Dark Mode</button>
      <Global
        styles={css`
          body {
            background-color: ${isDarkMode ? 'black' : 'white'};
            color: ${isDarkMode ? 'white' : 'black'};
          }
        `}
      />
    </div>
  )
}

export default DarkModeToggle

解説します。まず、 useStateを使ってisDarkModeという名前の状態を作ります。これはブール値(true か false)を持つことができます。デフォルトでは false(つまりライトモード)に設定されています。

次にボタンを作ります。このボタンがクリックされると、isDarkModeの値が反転します。つまり、もしisDarkModeが false なら true になり、もし true なら false になります。これでダークモードとライトモードを切り替えることができます。

最後にGlobalコンポーネントを使って全体のスタイルを変更します。bodyの背景色と文字色をisDarkModeの値によって変更します。もしisDarkModeが true(つまりダークモード)なら背景色は黒で文字色は白になります。逆にisDarkModeが false(つまりライトモード)なら背景色は白で文字色は黒になります。

これでダークモードとライトモードを切り替えることができるようになりました。

このように、三項演算子や論理演算子を使って、状態に応じてスタイルを変更することができるので、Emotion はとても便利ですね。

ブレークポイントを設定する

Emotion の Global Styles は基本的なスタイリングだけでなく、より高度なスタイリングにも対応しています。以下に、デバイスごとのブレークポイントを管理する例を紹介します。

// styles/GlobalStyles.tsx

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

const breakpoints = [576, 768, 992, 1200]

const mq = breakpoints.map((bp) => `@media (min-width: ${bp}px)`)

export const GlobalStyles = () => (
  <Global
    styles={css`
      body {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
      }

      ${mq[0]} {
        body {
          background-color: red;
        }
      }

      ${mq[1]} {
        body {
          background-color: yellow;
        }
      }

      ${mq[2]} {
        body {
          background-color: green;
        }
      }

      ${mq[3]} {
        body {
          background-color: blue;
        }
      }
    `}
  />
)

以上のコードでは、デバイスの幅ごとに異なる背景色を適用する Global Styles を定義しています。

Emotion の Global Styles を活用することで、一貫性のあるスタイルをアプリケーション全体に簡単に適用することが可能になります。

Next.js と TypeScript で、Emotion の Global Styles を理解する