Next.js + Shadcn + Tailwind + Lexical構成にダークテーマを導入した際のメモ
夜中に開発していると、暗めのエディタと真っ白なサイトの切り替えがかなりしんどい。
実装としても面白そうなので、ダークテーマ導入を優先してみることにする。
サイト構成
- framework: Next.js
- UI framework: Shadcn
- CSS framework: Tailwind
- その他: Lexical
Shadcn自体はもともとdark themeを導入しやすい仕組みになっている(後述)ので、Shadcnコンポーネントで構成している部分は殆ど調整が必要ないはず。
記事全般に使用しているLexicalは、Lexical独自のTheming機能を持っているので、そのあたりの調整が必要になってかなという事前の見積もり。
技術選定&導入
- next-themes: https://github.com/pacocoursey/next-themes
ダークテーマの基本的な機能に関してはnext-themesライブラリを使用。
選定理由
- <ThemeProvider>をRoot Layoutに適用するだけでサイト全体でuseTheme管理ができるようになる。
- ライト、ダーク、システムの状態をシンプルに扱うことができる。
- attribute="class"を指定することによってTailwindと連携がしやすい。
↓ライトモード時
<html class="light" style="color-scheme: light;">↓ダークモード時
<html class="dark" style="color-scheme: dark;">- localStorage + eventListenerによってタブ間でのテーマ共有も透過的に実装できる。
タブAでテーマを変更したときにテーマ設定をlocalStorageに書き込み、タブBはstorage eventをlistenすることによりテーマ変更を検知&反映できる。
スムーズに対応できたポイント
Shadcn dark mode: https://ui.shadcn.com/docs/dark-mode/next
Shadcn自体がnext-themesを用いたテーマ管理を紹介しているということもあり、shadcn系のコンポーネントは基本的にうまくいった。
lucide-reactのアイコンも調整不要だった。
colorのデフォルト値にcurrentColorが使用されているため、アイコンを描画する箇所における文字色が反映されるようになっている。
調整が必要だったポイント
hover:bg-gray-50のように、直接色を指定してしまっている箇所はhover:bg-accentに置き換えた。
accentという色トークン自体は、Shadcn導入で初期化を行うタイミングでtailwind.config.tsに追加されている。
theme: {
extend: {
...
colors: {
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
}
}
}
}--accentや--accent-foreground自体はglobal.css上に定義されている。
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;Lexical EditorのThemeに関しては、EditorThemeはテーマによって分岐せずに一つに保った。EditorThemeで参照するEditor.module.cssのほうでtokenを使いながらテーマに応じたスタイルを充てるようにした。
具体的なスタイルの分岐に関してはCSSのレイヤに押し込めた状態にできたので良い感じ。