ダークモード切り替え(Dark Mode Toggle)— CSS変数で実装
このコンポーネントについて
ダークモード切り替えは、背景・文字・装飾の配色を「ライト(明)/ダーク(暗)」で切り替えるUIです。目への負担を軽減し、暗い環境での視認性を高める効果があるため、近年のWebアプリで広く実装されています。
このページでは、CSS カスタムプロパティ(変数)を活用したシンプルな実装例を紹介します。コンテナ要素に .is-dark クラスを付け外しするだけで全配色を一括変更できます。右上の月アイコンをクリックするとダークモードに、太陽アイコンをクリックするとライトモードに切り替わります。
- デモエリア内限定の切り替え — ページ全体ではなく
.dm-cardコンテナ内だけにスコープを絞って切り替え。ページの他の部分には影響しない - CSS カスタムプロパティ制御 —
--dm-bg/--dm-textなどの変数を.is-darkクラスで上書きし、配色を一括変更 - 月/太陽アイコントグル — SVG アイコンを
.is-dark有無で表示切り替え。現在のモードを直感的に示す - localStorage でモード保持 — ブラウザに選択状態を保存。ページを閉じても次回開いたときに設定が復元される
- スムーズなトランジション —
transitionで色変化を 0.3s でアニメーション。ちらつきなく切り替わる
実装のポイント・注意点
CSS カスタムプロパティ(変数)は、親要素に定義した値をすべての子要素が参照できる仕組みです。.dm-card に --dm-bg: #ffffff のように定義しておき、.dm-card.is-dark で --dm-bg: #1a1f2e に上書きするだけで、カード内のすべての要素の背景色が一斉に切り替わります。個別の要素に直接カラーコードを書くより変更が1か所で済み、テーマ管理に非常に向いています。
アイコンの切り替えはJavaScriptではなくCSSで制御しています。.dm-icon-sun { display: none } でデフォルトは太陽を非表示にし、.dm-card.is-dark .dm-icon-moon { display: none } と .dm-card.is-dark .dm-icon-sun { display: block } でダークモード時に入れ替えます。JS側はクラスのトグルだけに集中できるため、コードがシンプルになります。
localStorage はブラウザにデータを保存しておける仕組みです。localStorage.setItem('dmMode', 'dark') で書き込み、localStorage.getItem('dmMode') で読み込めます。ページを閉じてもデータが残るため、「前回ダークモードにしていたら次回も同じ設定で開く」という動作を実現できます。サイト全体のダークモード設定と混在しないよう、キー名はコンポーネントに固有のもの(この例では 'dmMode')を使うのがおすすめです。
HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。
デモ
ダークモードの実装ガイド
ダークモードは、背景を暗くして文字を明るくする配色テーマです。目への負担を軽減し、暗い環境での視認性を高める効果があります。近年のWebアプリでは、ユーザーが自由に切り替えられる機能として広く実装されています。
CSS カスタムプロパティ(変数)を使うと、テーマの切り替えをシンプルに実装できます。コンテナにクラスを付け外しするだけで変数の値が一斉に切り替わり、すべての子要素の配色がまとめて変わります。
const card = document.getElementById('dmCard');
btn.addEventListener('click', () => {
card.classList.toggle('is-dark');
});
サンプルソース
3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js
— 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ダークモード切り替え サンプル</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="dm-card" id="dmCard">
<div class="dm-header">
<div class="dm-meta">
<span class="dm-author">by Admin</span>
<span class="dm-sep">·</span>
<time class="dm-date">2025-06-01</time>
</div>
<button class="dm-toggle-btn" id="dmToggle" aria-label="ダークモードを切り替え">
<!-- ライトモード時に表示される月アイコン -->
<svg class="dm-icon dm-icon-moon" width="18" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
<!-- ダークモード時に表示される太陽アイコン -->
<svg class="dm-icon dm-icon-sun" width="18" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="5"/>
<line x1="12" y1="1" x2="12" y2="3"/>
<line x1="12" y1="21" x2="12" y2="23"/>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
<line x1="1" y1="12" x2="3" y2="12"/>
<line x1="21" y1="12" x2="23" y2="12"/>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
</svg>
</button>
</div>
<p class="dm-title">ダークモードの実装ガイド</p>
<p class="dm-body">ダークモードは、背景を暗くして文字を明るくする配色テーマです。目への負担を軽減し、暗い環境での視認性を高める効果があります。</p>
<p class="dm-body">CSS カスタムプロパティ(変数)を使うと、コンテナにクラスを付け外しするだけで全配色を一括変更できます。</p>
<pre class="dm-code"><code>const card = document.getElementById('dmCard');
btn.addEventListener('click', () => {
card.classList.toggle('is-dark');
});</code></pre>
<div class="dm-tags">
<span class="dm-tag">JavaScript</span>
<span class="dm-tag">CSS変数</span>
<span class="dm-tag">アクセシビリティ</span>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
/* ===== CSS カスタムプロパティ(ライトモードのデフォルト値)===== */
.dm-card {
--dm-bg: #ffffff;
--dm-text: #1a2332;
--dm-subtext: #5a6a7a;
--dm-border: #e2e8f0;
--dm-code-bg: #f4f6f9;
--dm-code-text: #2b4066;
--dm-tag-bg: #e8f0fe;
--dm-tag-text: #2b7fe8;
--dm-toggle-bg: #e2e8f0;
}
/* ===== ダークモード(.is-dark でカスタムプロパティを上書き)===== */
.dm-card.is-dark {
--dm-bg: #1a1f2e;
--dm-text: #e2e8f4;
--dm-subtext: #8898aa;
--dm-border: #2d3548;
--dm-code-bg: #0d1117;
--dm-code-text: #79c0ff;
--dm-tag-bg: #1e3a5f;
--dm-tag-text: #79b8ff;
--dm-toggle-bg: #374151;
}
/* ===== 基本リセット ===== */
*, *::before, *::after { box-sizing: border-box; }
body {
font-family: sans-serif;
padding: 24px;
background: #f0f2f5;
}
/* ===== カード全体 ===== */
.dm-card {
background: var(--dm-bg);
border: 1.5px solid var(--dm-border);
border-radius: 12px;
padding: 24px;
max-width: 480px;
transition: background 0.3s, border-color 0.3s;
}
/* ===== ヘッダー(著者・日付・トグルボタン)===== */
.dm-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
}
.dm-meta {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--dm-subtext);
transition: color 0.3s;
}
/* ===== トグルボタン ===== */
.dm-toggle-btn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: var(--dm-toggle-bg);
border: none;
border-radius: 50%;
cursor: pointer;
color: var(--dm-subtext);
transition: background 0.3s, color 0.3s, opacity 0.15s;
flex-shrink: 0;
}
.dm-toggle-btn:hover { opacity: 0.75; }
/* ===== アイコン切り替え(CSSで制御)===== */
/* デフォルトは月アイコンを表示(ライトモード)、太陽は非表示 */
.dm-icon-sun { display: none; }
/* ダークモード時:月を非表示にして太陽を表示 */
.dm-card.is-dark .dm-icon-moon { display: none; }
.dm-card.is-dark .dm-icon-sun { display: block; }
/* ===== タイトル ===== */
.dm-title {
margin: 0 0 12px;
font-size: 18px;
font-weight: 700;
color: var(--dm-text);
line-height: 1.4;
transition: color 0.3s;
}
/* ===== 本文テキスト ===== */
.dm-body {
margin: 0 0 12px;
font-size: 14px;
color: var(--dm-subtext);
line-height: 1.7;
transition: color 0.3s;
}
/* ===== コードブロック ===== */
.dm-code {
background: var(--dm-code-bg);
border: 1px solid var(--dm-border);
border-radius: 6px;
padding: 12px 16px;
margin: 0 0 16px;
overflow-x: auto;
transition: background 0.3s, border-color 0.3s;
}
.dm-code code {
font-family: 'Consolas', 'Courier New', monospace;
font-size: 13px;
color: var(--dm-code-text);
line-height: 1.6;
transition: color 0.3s;
}
/* ===== タグバッジ ===== */
.dm-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.dm-tag {
padding: 3px 10px;
background: var(--dm-tag-bg);
color: var(--dm-tag-text);
border-radius: 20px;
font-size: 12px;
font-weight: 600;
transition: background 0.3s, color 0.3s;
}
var card = document.getElementById('dmCard');
var btn = document.getElementById('dmToggle');
// ページを開いたとき、前回保存したモードを読み込んで反映する
var savedMode = localStorage.getItem('dmMode');
if (savedMode === 'dark') {
card.classList.add('is-dark');
}
btn.addEventListener('click', function() {
card.classList.toggle('is-dark');
// 現在のモードをブラウザに保存しておく(ページを閉じても設定が残る)
var isDark = card.classList.contains('is-dark');
localStorage.setItem('dmMode', isDark ? 'dark' : 'light');
});
AI用プロンプト
以下のプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。
ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。ライブラリ指定や配色変更など、要件を追記して使うのがおすすめです。
※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。
💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。
# ダークモード切り替え 作成依頼
## 概要
コンテナ内だけでダークモードとライトモードを切り替えられるUIを作成してください。
ミニ記事カード(タイトル・本文・コードブロック・タグ)をデモコンテンツとして使います。
## 要件
- ページ全体ではなく、指定のコンテナ(.dm-card)内のみに切り替えをスコープする
- カード右上の月/太陽アイコンボタンで切り替える
- ライト→ダーク→ライトとトグル動作する
- CSS カスタムプロパティ(変数)で配色を管理し、.is-dark クラスの付け外しで切り替える
- 切り替え時に色変化を 0.3s のトランジションでアニメーションさせる
- localStorage を使ってページをまたいでも選択状態を保持する
## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:不要(固定幅カード)
## 動作詳細
- 初期表示はライトモード(ただし localStorage に dark が保存されていれば .is-dark を付与して起動)
- 月アイコンがライトモード時に表示。クリックでダークモードに切り替わり太陽アイコンに変わる
- 太陽アイコンがダークモード時に表示。クリックでライトモードに戻り月アイコンに変わる
- アイコンの切り替えは CSS の display プロパティで制御し、JavaScript では追加処理不要にする
- カード内のデモコンテンツ:記事タイトル・著者+日付・本文テキスト2段落・コードブロック・タグバッジ3つ
## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。