Next.jsのCSS入門:グローバルCSSとCSS Modulesの適用手順
Pages RouterでグローバルCSSとCSS Modulesを使う手順を解説。ファイルの配置、読み込み先、クラスの適用、スタイルの衝突を避ける考え方を紹介します。
Next.jsでは、アプリケーション全体の基本スタイルをグローバルCSSに、部品ごとの見た目をCSS Modulesに分けると管理しやすくなります。CSS Modulesはクラス名の衝突を避ける仕組みで、ファイル名を.module.cssで終わらせて利用します。
この記事はTypeScriptとPages Routerの構成を前提にします。ルートのpages/を使うプロジェクトでは、以下のパスからsrc/を除いて読み替えてください。
CSSを適用する手順
全体に適用するCSSを用意する
余白の初期値、文字の色、ボックスのサイズ計算などをsrc/styles/globals.cssへ書きます。
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #172033;
background: #f5f7fb;
font-family: system-ui, sans-serif;
line-height: 1.7;
}
button,
input {
font: inherit;
}
a {
color: #1d4ed8;
}
Pages Routerでは、グローバルCSSをsrc/pages/_app.tsxから読み込みます。既にこのファイルがあり、Providerやレイアウトを使っている場合は、それらを残したままimportを追加してください。次は最小構成です。
import type { AppProps } from 'next/app';
import '../styles/globals.css';
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
グローバルCSSのセレクターはページ全体へ影響します。たとえばbuttonの背景色をここで一律指定すると、別の機能のボタンにも適用されます。全体に必要な基礎だけに絞ると、後から上書きする量を減らせます。
CSS Modulesでカードの見た目を定義する
カード用にsrc/components/ArticleCard.module.cssを作成します。
.card {
width: min(100%, 40rem);
margin-inline: auto;
padding: clamp(1rem, 4vw, 2rem);
border: 1px solid #d8deea;
border-radius: 1rem;
background: #fff;
box-shadow: 0 0.5rem 1.5rem rgb(23 32 51 / 8%);
}
.title {
margin-block: 0 0.5rem;
font-size: clamp(1.25rem, 3vw, 1.75rem);
}
.link {
display: inline-block;
margin-top: 0.75rem;
text-underline-offset: 0.2em;
}
.link:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 4px;
}
width: min(100%, 40rem)で、小さい画面では親要素に収め、大きい画面では幅を制限しています。キーボード操作でリンクの位置が分かるよう、フォーカスの枠も残しています。
TypeScriptのコンポーネントから読み込む
src/components/ArticleCard.tsxを作成します。
import Link from 'next/link';
import styles from './ArticleCard.module.css';
type ArticleCardProps = {
title: string;
description: string;
href: string;
};
export default function ArticleCard({
title,
description,
href,
}: ArticleCardProps) {
return (
<article className={styles.card}>
<h2 className={styles.title}>{title}</h2>
<p>{description}</p>
<Link className={styles.link} href={href}>記事を読む</Link>
</article>
);
}
CSSの.cardは、importされたstyles.cardから参照します。ビルド時には衝突しにくいクラス名へ変換されます。別のCSS Moduleで同じ.cardという名前を使っても、それぞれのローカルクラスとして扱われます。
ただし、CSS ModulesはShadow DOMのような完全な隔離ではありません。グローバルCSS、継承されるプロパティ、:globalで指定したルールは影響し得ます。また、同じModuleから取得したクラスを複数の部品に付ければ、当然それらすべてにスタイルが適用されます。
ページで表示する
src/pages/index.tsxからカードを読み込みます。この例のリンク先は、同じプロジェクトで作成する紹介ページの/aboutです。
import ArticleCard from '../components/ArticleCard';
export default function HomePage() {
return (
<main style={{ padding: '2rem 1rem' }}>
<h1>開発ノート</h1>
<ArticleCard
title="このサイトについて"
description="学習内容とサイトの使い方を紹介します。"
href="/about"
/>
</main>
);
}
表示確認では画面幅を変え、文字の折り返し、横スクロール、キーボードでのフォーカスを確認します。CSSをimportしただけではクラスは付きません。見た目が変わらないときは、className={styles.card}の指定と、CSS側のクラス名が一致するか調べます。
App Routerとの違いと追加ライブラリ
App Routerでは、全体用CSSを通常src/app/layout.tsxから読み込みます。CSSを使うためだけに'use client'を付ける必要はありません。CSS Modulesはどちらのルーターでも利用できます。
通常のCSSとCSS Modulesは追加パッケージなしで使えます。Sassを使う場合は別途sassが必要です。Lessが標準でそのまま使えるという意味ではありません。最初は標準のCSSで構成を理解してから、必要な仕組みを追加すると判断しやすくなります。
関連記事と公式資料
部品の作り方はコンポーネントの作成と使用方法、全体の配置はディレクトリ構成と役割も参照してください。
スタイルが反映されないときの調べ方
読み込みとクラス名を確認する
CSSファイルが読み込まれているか、classNameに期待するクラスが付いているかを開発者ツールで確認します。CSS Modulesのクラスを文字列だけで指定せず、読み込んだオブジェクトから参照してください。
小さい画面でも操作する
デスクトップで収まっていても、長い見出しやコードで横にはみ出す場合があります。画面幅を変え、文字の折り返し、コード欄のスクロール、ボタンの操作を確認します。