<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dark Mode on kenji.blog</title><link>http://kenji.blog/tags/dark-mode/</link><description>Recent content in Dark Mode on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ja</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 12:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/tags/dark-mode/index.xml" rel="self" type="application/rss+xml"/><item><title>ブログのダークモード対応におけるCSS設計のポイント</title><link>http://kenji.blog/p/dark-mode-css-design-patterns/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/p/dark-mode-css-design-patterns/</guid><description>&lt;img src="http://kenji.blog/p/dark-mode-css-design-patterns/img/eyecatch.jpg" alt="Featured image of post ブログのダークモード対応におけるCSS設計のポイント" />&lt;p>現代のWeb開発において、ダークモード（Dark Mode）のサポートは単なる「あれば嬉しい機能（Nice to have）」から、ユーザー体験（UX）を向上させるための「必須要件（Must have）」へと変化しています。特にブログやドキュメントサイトのように長時間のテキストリーディングを前提とするメディアでは、ユーザーの眼精疲労を軽減し、デバイスのバッテリー消費を抑える効果があるため、ダークモード対応の重要性は極めて高いと言えます。&lt;/p>
&lt;p>本記事では、ブログのダークモード対応において避けては通れない技術的な課題と、保守性の高いCSS設計のポイントについて、フロントエンドエンジニアの視点から非常に深く掘り下げて解説します。CSS Custom Properties（CSS変数）の活用、FOUC（Flash of Unstyled Content）を防ぐための高度なJavaScript制御とSSR連携、アクセシビリティ（WCAG 2.1 AAA）を担保するための色彩設計（RGB, HSL, そして最新のOKLCH）、さらにはTailwind CSSを用いた実践的なコード例まで、ダークモード実装のすべてを網羅します。&lt;/p>
&lt;hr>
&lt;h2 id="1-css-custom-propertiescss変数によるテーマ設計の基礎">1. CSS Custom Properties（CSS変数）によるテーマ設計の基礎
&lt;/h2>&lt;p>ダークモードを実装する上で、現在もっとも標準的かつ強力な手法が &lt;strong>CSS Custom Properties（CSS変数）&lt;/strong> を利用したアプローチです。SassなどのCSSプリプロセッサの変数（&lt;code>$color&lt;/code>）がコンパイル時に静的に解決されるのに対し、CSS変数はブラウザのランタイムで動的に解決・上書きされます。これにより、JavaScriptからクラスを切り替えるだけで、ページ全体の色合いを瞬時に変更することが可能になります。&lt;/p>
&lt;h3 id="11-基本的なカラーテーマの定義">1.1 基本的なカラーテーマの定義
&lt;/h3>&lt;p>まずは、&lt;code>:root&lt;/code> 疑似クラスを用いて、ライトモード（デフォルト）のカラーパレットを定義します。そして、&lt;code>[data-theme='dark']&lt;/code> のような属性（あるいは &lt;code>.dark&lt;/code> クラス）が付与された際に、それらの変数を上書きするという設計パターンが王道です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* ライトモード（デフォルト）の変数定義 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f3f4f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#4b5563&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#3b82f6&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e5e7eb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* ダークモード時の変数上書き */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* 実際の適用 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">body&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">bg&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">primary&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">background-color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">color&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">a&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">color&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このように、レイアウトやタイポグラフィの指定と、色（テーマ）の指定を完全に分離することで、CSSの保守性は飛躍的に向上します。&lt;/p>
&lt;h3 id="12-media-prefers-color-scheme-dark-の活用">1.2 @media (prefers-color-scheme: dark) の活用
&lt;/h3>&lt;p>OSレベルでダークモードが設定されている場合、Webサイト初回訪問時から自動的にダークテーマを適用することがUXの観点から望ましいです。これを実現するのが &lt;code>@media (prefers-color-scheme: dark)&lt;/code> というメディアクエリです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* OSの環境設定がダークモードの場合のフォールバック */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">@&lt;/span>&lt;span class="k">media&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="nt">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nt">dark&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#111827&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-bg-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1f2937&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-primary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9fafb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-text-secondary&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9ca3af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#60a5fa&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--color-border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#374151&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この記法では、ユーザーが明示的にライトモードを選択（&lt;code>data-theme='light'&lt;/code>）していない限り、OSのダークモード設定を尊重して変数を上書きします。&lt;/p>
&lt;hr>
&lt;h2 id="2-色空間の理解とアクセシビリティwcag-21-aaa">2. 色空間の理解とアクセシビリティ（WCAG 2.1 AAA）
&lt;/h2>&lt;p>ダークモードの色彩設計において、単に「背景を黒く、文字を白くする」だけでは不十分です。コントラストが強すぎるとハレーションを起こして逆に読みにくくなり、コントラストが低すぎると視認性が損なわれます。Web Content Accessibility Guidelines (WCAG) では、視認性を確保するためのコントラスト比が厳密に定義されています。&lt;/p>
&lt;h3 id="21-wcag-コントラスト比の計算式">2.1 WCAG コントラスト比の計算式
&lt;/h3>&lt;p>WCAG におけるコントラスト比（Contrast Ratio） $CR$ は、背景色と前景色の相対輝度（Relative Luminance）を用いて以下のように定義されます。&lt;/p>
$$CR = \frac{L_{lighter} + 0.05}{L_{darker} + 0.05}$$&lt;p>ここで、$L_{lighter}$ は明るい方の色の相対輝度、$L_{darker}$ は暗い方の色の相対輝度です（値の範囲は 0.0 から 1.0 まで）。WCAG 2.1 のレベル AAA を達成するためには、通常のテキストで &lt;strong>7:1 以上&lt;/strong>、大きなテキストで &lt;strong>4.5:1 以上&lt;/strong> のコントラスト比が求められます。&lt;/p>
&lt;p>相対輝度 $L$ は、sRGB 色空間のRGB値から以下の複雑な数式で計算されます。&lt;/p>
$$L = 0.2126 \times R + 0.7152 \times G + 0.0722 \times B$$&lt;p>各成分（$R, G, B$）は、元の8ビット値（$R_{sRGB}$）を255で割った正規化値を用いて、ガンマ補正を解くための以下の変換を行います。&lt;/p>
$$
R, G, B =
\begin{cases}
\frac{C_{sRGB}}{12.92} &amp; \text{if } C_{sRGB} \le 0.03928 \\
\left( \frac{C_{sRGB} + 0.055}{1.055} \right)^{2.4} &amp; \text{otherwise}
\end{cases}
$$&lt;p>この計算を手動で行うのは困難ですが、色彩設計ツールを活用することで、コントラスト比が 7:1 ($CR \ge 7.0$) を満たす色を機械的に選定できます。&lt;/p>
&lt;h3 id="22-hsl-vs-rgb-vs-oklch">2.2 HSL vs RGB vs OKLCH
&lt;/h3>&lt;p>カラーパレットを作成する際、かつては RGB や HSL が主流でした。しかし、これらには「知覚的な均一性」という観点で大きな欠陥があります。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>RGB&lt;/strong>: 機械的な光の三原色であり、人間が直感的に「明るくする」「暗くする」といった調整を行うのが困難です。&lt;/li>
&lt;li>&lt;strong>HSL&lt;/strong>: 色相 (Hue)、彩度 (Saturation)、輝度 (Lightness) を用いますが、HSL の「輝度 (L)」は人間の目の知覚的な明るさと一致しません。例えば、HSLで輝度50%の純粋な黄色と純粋な青は、数値上は同じ明るさですが、人間の目には黄色の方が圧倒的に明るく見えます。&lt;/li>
&lt;li>&lt;strong>OKLCH&lt;/strong>: 近年 CSS Color Module Level 4 で導入された最新の色空間です。Lightness (知覚的明度)、Chroma (彩度)、Hue (色相) で構成されており、&lt;strong>人間の視覚特性に完全に一致（知覚的均一）&lt;/strong> しています。&lt;/li>
&lt;/ul>
&lt;p>OKLCH を使用することで、色相（Hue）を変えても同じ知覚的明度（Lightness）を保つことができるため、ダークモード用のカラーパレット生成が極めて予測可能かつ安全になります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="c">/* OKLCHを用いたCSS変数の定義例 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* ライトモードのベース明度を高く、彩度を抑えめに */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.98&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.25&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.65&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* ダークモードでは明度を反転させるだけで、知覚的コントラストを維持しやすい */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--bg-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.20&lt;/span> &lt;span class="mf">0.02&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-base&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.95&lt;/span> &lt;span class="mf">0.01&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--primary-brand&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">oklch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.75&lt;/span> &lt;span class="mf">0.15&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c">/* ダークモード用に少し明るくして視認性を確保 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>このようにOKLCHを採用することで、複数のテーマ間で一貫したコントラスト比（WCAG AAA水準）を担保するロジックをシンプルに構築できます。&lt;/p>
&lt;hr>
&lt;h2 id="3-foucflash-of-unstyled-contentの防止とssrハイドレーション">3. FOUC（Flash of Unstyled Content）の防止とSSRハイドレーション
&lt;/h2>&lt;p>ダークモード対応で最も開発者を悩ませるのが、&lt;strong>FOUC（Flash of Unstyled Content）&lt;/strong> と呼ばれる画面のチラつき問題です。&lt;/p>
&lt;h3 id="31-クライアントサイドjsによるテーマ切り替えの罠">3.1 クライアントサイドJSによるテーマ切り替えの罠
&lt;/h3>&lt;p>ReactやVueなどのSPA（あるいはSSGによる静的サイト）において、ユーザーの設定を &lt;code>localStorage&lt;/code> に保存し、JavaScriptで読み込んでテーマを切り替える手法が一般的です。しかし、この処理をReactの &lt;code>useEffect&lt;/code> などで行うと、以下のような問題が発生します。&lt;/p>
&lt;ol>
&lt;li>ブラウザがライトモードのHTML/CSSを描画する。&lt;/li>
&lt;li>JSのバンドルが読み込まれ、実行される。&lt;/li>
&lt;li>&lt;code>localStorage&lt;/code> から &lt;code>dark&lt;/code> 設定を読み取る。&lt;/li>
&lt;li>HTMLに &lt;code>dark&lt;/code> クラスが付与され、画面が突然暗くなる（チラつき）。&lt;/li>
&lt;/ol>
&lt;h3 id="32-完璧なfouc防止策cookieとssrの活用">3.2 完璧なFOUC防止策：CookieとSSRの活用
&lt;/h3>&lt;p>FOUCを完全に防止し、ハイドレーションのエラーを防ぐためのベストプラクティスは、&lt;strong>ユーザーのテーマ設定を &lt;code>document.cookie&lt;/code> に保存し、サーバーサイドレンダリング（SSR）の段階で適切なクラスを付与したHTMLを返す&lt;/strong> ことです。&lt;/p>
&lt;p>以下のシーケンス図は、Cookieを利用したテーマ初期化の理想的なフローを示しています。&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant User as &amp;#34;User&amp;#34;
participant Browser as &amp;#34;Browser OS&amp;#34;
participant Server as &amp;#34;Server SSR&amp;#34;
participant DOM as &amp;#34;Client DOM&amp;#34;
User-&amp;gt;&amp;gt;Browser: &amp;#34;Request Page&amp;#34;
Browser-&amp;gt;&amp;gt;Server: &amp;#34;GET request with Cookie (theme=dark)&amp;#34;
Server--&amp;gt;&amp;gt;Browser: &amp;#34;Return HTML with &amp;lt;html data-theme=&amp;#39;dark&amp;#39;&amp;gt;&amp;#34;
Browser-&amp;gt;&amp;gt;DOM: &amp;#34;Render Initial HTML (No FOUC)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;Execute Hydration Script&amp;#34;
alt &amp;#34;Cookie absent (First Visit)&amp;#34;
DOM-&amp;gt;&amp;gt;Browser: &amp;#34;Check OS prefers-color-scheme&amp;#34;
Browser--&amp;gt;&amp;gt;DOM: &amp;#34;matchMedia result (e.g. dark)&amp;#34;
DOM-&amp;gt;&amp;gt;DOM: &amp;#34;Set data-theme=&amp;#39;dark&amp;#39; via inline blocking script&amp;#34;
end
&lt;/pre>
&lt;h3 id="33-インラインスクリプトによる防衛線cookieが使えない静的サイトの場合">3.3 インラインスクリプトによる防衛線（Cookieが使えない静的サイトの場合）
&lt;/h3>&lt;p>SSG（静的サイト生成）のみでSSRが不可能なブログ（HugoやGatsby, Astroの静的エクスポートなど）の場合は、&lt;code>&amp;lt;head&amp;gt;&lt;/code> タグの内部にブロッキング実行されるインラインのJavaScriptを配置し、DOMが描画される直前にクラスを付与する手法が必須となります。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- &amp;lt;head&amp;gt; 内の最後に配置する --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">osTheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localTheme&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">osTheme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setAttribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;data-theme&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この小さなスクリプトは、ブラウザのレンダリングをブロックして即座に実行されるため、画面が描画される時点では既に &lt;code>data-theme&lt;/code> 属性がセットされており、画面のチラつき（FOUC）を完全に防ぐことができます。&lt;/p>
&lt;hr>
&lt;h2 id="4-tailwind-css-と生のscsscssでの実装アプローチ">4. Tailwind CSS と生のSCSS/CSSでの実装アプローチ
&lt;/h2>&lt;p>ダークモードを実際のプロジェクトに組み込む際、ツールごとのアプローチを理解しておく必要があります。&lt;/p>
&lt;h3 id="41-tailwind-css-におけるダークモード">4.1 Tailwind CSS におけるダークモード
&lt;/h3>&lt;p>Tailwind CSS は、デフォルトで &lt;code>dark:&lt;/code> バリアントを提供しており、非常に簡単にダークモードを実装できます。設定ファイル（&lt;code>tailwind.config.js&lt;/code>）で &lt;code>darkMode&lt;/code> プロパティを設定します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// tailwind.config.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">module&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">exports&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// &amp;#39;media&amp;#39; (OS設定依存) または &amp;#39;class&amp;#39; (手動切り替え可能)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">darkMode&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;class&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">theme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">extend&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">colors&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* CSS変数を利用してTailwindのカラーパレットを拡張 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">primary&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-primary) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">background&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;rgb(var(--color-background) / &amp;lt;alpha-value&amp;gt;)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>HTML側では以下のようにクラスを付与するだけです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">div&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">h1&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text-2xl font-bold&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Hello World&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">h1&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;mt-2&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>Tailwind makes dark mode incredibly easy.&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">div&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>しかし、すべての要素に &lt;code>dark:bg-xxx&lt;/code> と記述するのはコンポーネントが肥大化する原因にもなります。大規模なブログやアプリでは、&lt;strong>CSS変数を基盤とし、TailwindからはそのCSS変数を参照する&lt;/strong> というハイブリッドな設計（セマンティックカラー設計）が推奨されます。&lt;/p>
&lt;p>以下は、CSS変数の継承と適用レイヤーを示すクラス図です。&lt;/p>
&lt;pre class="mermaid">
classDiagram
class GlobalCSSVariables {
&amp;#34;--color-brand-500&amp;#34;
&amp;#34;--color-gray-900&amp;#34;
}
class SemanticVariables {
&amp;#34;--bg-primary&amp;#34;
&amp;#34;--text-base&amp;#34;
&amp;#34;--accent&amp;#34;
}
class TailwindConfig {
&amp;#34;theme.colors.background&amp;#34;
&amp;#34;theme.colors.primary&amp;#34;
}
class UIComponents {
&amp;#34;class=&amp;#39;bg-background text-primary&amp;#39;&amp;#34;
}
GlobalCSSVariables &amp;lt;|-- SemanticVariables : &amp;#34;:root &amp;amp; .dark&amp;#34;
SemanticVariables &amp;lt;|-- TailwindConfig : &amp;#34;tailwind.config.js&amp;#34;
TailwindConfig &amp;lt;.. UIComponents : &amp;#34;Applies Utility Classes&amp;#34;
&lt;/pre>
&lt;h3 id="42-raw-scsscss-での実装mixinの活用">4.2 Raw SCSS/CSS での実装（Mixinの活用）
&lt;/h3>&lt;p>Tailwindを使用せず、独自にSCSSを記述するプロジェクトでは、&lt;code>@mixin&lt;/code> を活用してダークモードのスタイルをカプセル化します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-scss" data-lang="scss">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* SCSS Mixinの定義 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">@mixin&lt;/span>&lt;span class="nf"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* [data-theme=&amp;#39;dark&amp;#39;] 属性、または OS設定 の両方をサポート */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@media&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">prefers-color-scheme&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="ni">dark&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">:root:not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;light&amp;#39;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="k">&amp;amp;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@content&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 使用例 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nc">.card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#ffffff&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#333333&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="ni">solid&lt;/span> &lt;span class="mh">#eeeeee&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">@include&lt;/span>&lt;span class="nd"> dark-mode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">background-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#1a202c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#e2e8f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">border-color&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="mh">#2d3748&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>この方法は直感的ですが、コンパイル後のCSSファイルサイズが肥大化しやすい（メディアクエリが各セレクタに複製される）ため、やはりCSS変数（Custom Properties）を中心とした設計への移行が現在のトレンドです。&lt;/p>
&lt;hr>
&lt;h2 id="5-画像imageと-svg-のダークモード最適化">5. 画像（Image）と SVG のダークモード最適化
&lt;/h2>&lt;p>テキストや背景の色彩設計が完了しても、コンテンツとして配置されている画像やアイコン（SVG）がライトモードのままだと、ダークモード時に非常に眩しく浮いて見えてしまいます。これらに対する最適化も不可欠です。&lt;/p>
&lt;h3 id="51-画像の輝度を抑える-css-フィルタ">5.1 画像の輝度を抑える CSS フィルタ
&lt;/h3>&lt;p>写真などのビットマップ画像は、ダークモード時にそのまま表示すると眩しすぎることがあります。CSSの &lt;code>filter&lt;/code> プロパティを使用して、画像の輝度（brightness）とコントラスト（contrast）をわずかに下げることで、ダークテーマのUIに自然に馴染ませることができます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">not&lt;/span>&lt;span class="o">([&lt;/span>&lt;span class="nt">src&lt;/span>&lt;span class="o">*=&lt;/span>&lt;span class="s2">&amp;#34;.svg&amp;#34;&lt;/span>&lt;span class="o">])&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* 明るさを下げ、少しコントラストを上げる */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">0.8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1.1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">transition&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="k">filter&lt;/span> &lt;span class="mf">0.3&lt;/span>&lt;span class="kt">s&lt;/span> &lt;span class="kc">ease&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="nt">data-theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="o">]&lt;/span> &lt;span class="nt">img&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">/* ホバー時は元の明るさに戻す（ユーザーが詳細を見たい場合） */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">brightness&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nb">contrast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-picture-タグによる画像の出し分け">5.2 &lt;code>&amp;lt;picture&amp;gt;&lt;/code> タグによる画像の出し分け
&lt;/h3>&lt;p>ロゴ画像や、説明用の図解（背景が白で固定されているJPEGなど）は、フィルタ処理だけでは対応できません。その場合は、HTMLの &lt;code>&amp;lt;picture&amp;gt;&lt;/code> 要素とメディアクエリを使用して、ダークモード用の別の画像ファイルを出し分けるのが正解です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- ダークモードOS設定のユーザーにはこちらを表示 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">source&lt;/span> &lt;span class="na">srcset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-dark.png&amp;#34;&lt;/span> &lt;span class="na">media&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- デフォルト（ライトモード） --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">img&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/img/logo-light.png&amp;#34;&lt;/span> &lt;span class="na">alt&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;サイトのロゴ&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">picture&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>※ただし、この方法は &lt;code>localStorage&lt;/code> などによる手動トグルとは連動しない（OS設定にのみ依存する）ため、手動切り替えを実装している場合は、JSで画像の &lt;code>src&lt;/code> を動的に書き換えるか、CSSクラスで &lt;code>display: none&lt;/code> を切り替える必要があります。&lt;/p>
&lt;h3 id="53-svgアイコンの-currentcolor-対応">5.3 SVGアイコンの &lt;code>currentColor&lt;/code> 対応
&lt;/h3>&lt;p>アイコン等で使用するインラインSVGは、塗りつぶしの色を親要素のテキストカラーに連動させるのが最もスマートです。SVGの &lt;code>fill&lt;/code> や &lt;code>stroke&lt;/code> 属性に &lt;code>currentColor&lt;/code> を指定します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- CSSの color プロパティの値（var(--text-primary)など）が自動的に適用される --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">svg&lt;/span> &lt;span class="na">viewBox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;0 0 24 24&amp;#34;&lt;/span> &lt;span class="na">fill&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;currentColor&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">path&lt;/span> &lt;span class="na">d&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;M12 2L2 22h20L12 2z&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">svg&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これにより、ダークモードへ切り替わって親要素の文字色が白系統になれば、SVGアイコンも自動的に白系統に変化します。&lt;/p>
&lt;hr>
&lt;h2 id="6-まとめ持続可能なダークモード設計に向けて">6. まとめ：持続可能なダークモード設計に向けて
&lt;/h2>&lt;p>ブログやWebアプリケーションにおいて、高品質なダークモードを実装するためには、以下のポイントを網羅したCSS設計が不可欠です。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>CSS Custom Properties を活用する&lt;/strong>: 色指定のハードコードを避け、セマンティックな変数名（例: &lt;code>--bg-primary&lt;/code>）に抽象化する。&lt;/li>
&lt;li>&lt;strong>OKLCH 色空間を採用する&lt;/strong>: WCAG 2.1 AAA を満たすアクセシビリティの高いコントラスト比（7:1以上）を、知覚的に均一な色空間でロジカルに設計する。&lt;/li>
&lt;li>&lt;strong>FOUC対策を徹底する&lt;/strong>: SSRとCookieの連携、あるいは &lt;code>&amp;lt;head&amp;gt;&lt;/code> 内のブロッキング・インラインスクリプトにより、初回ロード時の画面のチラつきを完全に排除する。&lt;/li>
&lt;li>&lt;strong>メディアとアセットの最適化&lt;/strong>: &lt;code>filter: brightness()&lt;/code> や &lt;code>currentColor&lt;/code>、&lt;code>&amp;lt;picture&amp;gt;&lt;/code> タグを駆使して、テキスト以外の要素もダークテーマに調和させる。&lt;/li>
&lt;/ol>
&lt;p>単なる「色の反転」を超えたこれらの細やかな配慮こそが、ユーザーに長く愛され、目の疲れにくい優れた読書体験（リーディング・エクスペリエンス）を提供するモダンなブログの条件と言えるでしょう。これからダークモードを導入する開発者の方は、ぜひ本記事の設計パターンを参考にしてみてください。&lt;/p></description></item></channel></rss>