第56章:ダークモードの方針:CSSでやる?クラスでやる?🌙✨
ダークモードって「暗い配色に切り替える」だけじゃなくて、“どうやって切り替える設計にするか” が超大事だよ〜!😆🖤 この章では、Next.js(App Router)でよく使う2つの方針を、迷わないように整理していくね🧭✨
この章のゴール🎯
- ダークモードの実装方針を2種類(CSS / クラス)で理解する🌙
- どっちを選ぶべきか判断できるようになる✅
- Next.jsで「それっぽく動く」最小実装ができる💪✨
ダークモードの方針は大きく2つ🌙🔀
① CSSだけでやる(OS設定に合わせる)🧊
- OS/ブラウザの設定(ライト/ダーク)に自動追従✨
- JavaScriptいらない!👍
- でも…手動切り替えボタンは作りにくい(作れなくはないけど、結局JSが必要になりがち)😵💫
prefers-color-schemeっていう仕組みを使うよ〜! (MDN Web Docs)
② クラスでやる(dark を付け替える)🎮
htmlやbodyにdarkクラスを付けたり外したりして切り替える💡- 手動トグル(ボタン)も、保存(localStorage)もやりやすい😍
- ただし、Next.jsだと**最初の表示で一瞬チカッと色が変わる問題(フラッシュ)**に気を配る必要あり⚠️
どっちにする?迷ったらこれでOK🧭🌙
まず結論:学習用&実務寄りなら「クラス方式」がおすすめ💖
理由はこれ👇
- ボタンで切り替えできる🔘✨
- 好みを保存できる💾
- Tailwindでも一般的にクラス方式がよく使われるよ〜! (tailwindcss.com)
実装①:CSSだけでダーク対応(最小・自動)🧊🌙
✅ app/globals.css に書く(例)
ポイントは CSS変数 で色をまとめること!🎨✨
/* app/globals.css */
:root {
color-scheme: light dark; /* ブラウザのフォーム部品にもヒントを出す */
--bg: #ffffff;
--text: #111111;
--card: #f4f4f5;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0b0f19;
--text: #e7e9ee;
--card: #111827;
}
}
body {
background: var(--bg);
color: var(--text);
}
.card {
background: var(--card);
padding: 16px;
border-radius: 12px;
}
prefers-color-scheme はOS/ブラウザの設定に合わせてくれるよ〜!(MDN Web Docs)
あと color-scheme を入れると、入力欄とかスクロールバー系が馴染みやすくなることがあるよ✨ (MDN Web Docs)
実装②:クラス方式で「手動切り替え」までやる🎮🌙(おすすめ)
Next.js(App Router)で「チカつき」や「Hydration警告」を避けたいなら、next-themes がラクで安定しやすいよ🧰✨
(<html> に suppressHydrationWarning が必要、って注意も公式に書かれてるよ)(GitHub)
Hydrationの考え方自体はNext.js公式の注意も見ておくと安心!(nextjs.org)
1) インストール💿✨(PowerShellでOK)
npm i next-themes
2) app/providers.tsx を作る(Client Component)🧩
"use client";
import { ThemeProvider } from "next-themes";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider
attribute="class" // html に class="dark" を付ける方式
defaultTheme="system"
enableSystem
>
{children}
</ThemeProvider>
);
}
3) app/layout.tsx で包む🧱✨
import "./globals.css";
import { Providers } from "./providers";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja" suppressHydrationWarning>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
next-themes が html を書き換えるので、suppressHydrationWarning が必要になることがあるよ〜!(GitHub)
4) ダーク/ライトの色を「クラス」で切り替えるCSSを書く🎨🌙
/* app/globals.css */
:root {
color-scheme: light dark;
--bg: #ffffff;
--text: #111111;
--card: #f4f4f5;
}
/* html に dark クラスが付いたら上書き */
html.dark {
--bg: #0b0f19;
--text: #e7e9ee;
--card: #111827;
}
body {
background: var(--bg);
color: var(--text);
}
.card {
background: var(--card);
padding: 16px;
border-radius: 12px;
}
5) トグルボタンを作る🔘✨(components/ThemeToggle.tsx)
最初の表示ズレ防止で「マウント後に表示」するのがコツだよ〜!🫶 (Hydrationのズレ回避の考え方は公式メッセージも参考になるよ)(nextjs.org)
"use client";
import { useEffect, useState } from "react";
import { useTheme } from "next-themes";
export function ThemeToggle() {
const { theme, setTheme, resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null; // 最初は表示しない(ズレ防止)
const current = theme === "system" ? resolvedTheme : theme;
return (
<button
type="button"
onClick={() => setTheme(current === "dark" ? "light" : "dark")}
style={{
padding: 12,
borderRadius: 12,
border: "1px solid #ccc",
background: "transparent",
cursor: "pointer",
}}
>
{current === "dark" ? "🌙 ダーク" : "☀️ ライト"}(タップで切替)
</button>
);
}
6) app/page.tsx に置いて動作確認🏁✨
import { ThemeToggle } from "@/components/ThemeToggle";
export default function Page() {
return (
<main style={{ padding: 24 }}>
<h1>ダークモード練習🌙✨</h1>
<ThemeToggle />
<div className="card" style={{ marginTop: 16 }}>
カードだよ〜🫶 背景色が切り替わればOK!
</div>
</main>
);
}
まとめ:この章の“方針”だけ覚えれば勝ち🏆🌙
- 自動でOKなら →
prefers-color-scheme(CSSだけ)🧊 (MDN Web Docs) - 手動切替したいなら → クラス方式(
html.darkなど)🎮 - Next.jsで安定させたいなら →
next-themesが便利🧰(Hydration注意)(GitHub) - Tailwindでも「クラス方式」がよく使われるよ〜🌙✨ (tailwindcss.com)
ちょい練習💪💕(1分)
- ボタンの文言を「🌙 Moon / ☀️ Sun」にしてみよ〜😆
.cardにbox-shadowを付けて、ライト/ダークで影の濃さを変えてみよ〜✨