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
というライブラリから Global
と css
というものを取り出して使います。このライブラリは、ウェブサイト全体に適用されるスタイルを作るのを助けてくれるツールです。
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 を活用することで、一貫性のあるスタイルをアプリケーション全体に簡単に適用することが可能になります。