ダークモード実装ガイド

Asagiriで簡単にダークモードを実装する完全ガイド

← ドキュメントに戻る

クイックスタート(3ステップ)

以下のコードをコピーするだけで、すぐにダークモードが使えます!

1HTMLにdata-theme属性を追加

<!DOCTYPE html>
<html lang="ja" data-theme="light">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="path/to/asagiri.min.css">
</head>
<body>
    <!-- ここにコンテンツ -->
</body>
</html>

2テーマ切り替えボタンを追加

<button class="btn btn-primary" onclick="toggleTheme()">
    テーマ切替
</button>

3JavaScriptを追加

<script>
function toggleTheme() {
    const html = document.documentElement;
    const currentTheme = html.getAttribute('data-theme');
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
    html.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
}

// ページ読み込み時にテーマを復元
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
</script>
完了! これだけでダークモードが動作します。右上のボタンで試してみてください。

完全なHTMLサンプル

このコードをコピーして、新しいHTMLファイルに貼り付けるだけで動作します。

<!DOCTYPE html>
<html lang="ja" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ダークモード対応ページ</title>
    <link rel="stylesheet" href="dist/asagiri.min.css">
    <style>
        body {
            background-color: var(--color-bg);
            color: var(--color-text);
            min-height: 100vh;
            padding: 2rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <header style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;">
            <h1>マイアプリ</h1>
            <button class="btn btn-primary" onclick="toggleTheme()">
                テーマ切替
            </button>
        </header>

        <div class="card">
            <div class="card-header">
                <h2>ダークモード対応カード</h2>
            </div>
            <div class="card-body">
                <p>このカードは自動的にダークモードに対応します。</p>
                <button class="btn btn-success">成功</button>
                <button class="btn btn-danger">エラー</button>
            </div>
        </div>
    </div>

    <script>
        function toggleTheme() {
            const html = document.documentElement;
            const currentTheme = html.getAttribute('data-theme');
            const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
            html.setAttribute('data-theme', newTheme);
            localStorage.setItem('theme', newTheme);
        }

        // ページ読み込み時にテーマを復元
        const savedTheme = localStorage.getItem('theme') || 'light';
        document.documentElement.setAttribute('data-theme', savedTheme);
    </script>
</body>
</html>

システムのダークモード設定に従う

ユーザーのOSのダークモード設定を自動的に検出して適用できます。

<script>
// システムのダークモード設定を検出
function getPreferredTheme() {
    // 保存されたテーマを優先
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
        return savedTheme;
    }

    // システム設定を検出
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
        return 'dark';
    }

    return 'light';
}

// テーマを適用
const preferredTheme = getPreferredTheme();
document.documentElement.setAttribute('data-theme', preferredTheme);

// システム設定の変更を監視
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
    // ユーザーが手動で設定していない場合のみ変更
    if (!localStorage.getItem('theme')) {
        const newTheme = e.matches ? 'dark' : 'light';
        document.documentElement.setAttribute('data-theme', newTheme);
    }
});

// テーマ切り替え関数
function toggleTheme() {
    const html = document.documentElement;
    const currentTheme = html.getAttribute('data-theme');
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
    html.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
}
</script>
ポイント:このコードは、ユーザーが手動で切り替えた場合はその設定を優先し、そうでない場合はシステム設定に従います。

アニメーション付きトグルボタン

よりリッチなテーマ切り替えボタンの実装例です。

<button class="btn btn-primary" onclick="toggleThemeWithAnimation()">
    <span id="themeText">ダークモード</span>
</button>

<script>
function toggleThemeWithAnimation() {
    const html = document.documentElement;
    const currentTheme = html.getAttribute('data-theme');
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

    // テーマを変更
    html.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);

    // ボタンのテキストを更新
    const text = document.getElementById('themeText');

    if (newTheme === 'dark') {
        text.textContent = 'ライトモード';
    } else {
        text.textContent = 'ダークモード';
    }
}

// 初期化
window.addEventListener('DOMContentLoaded', () => {
    const currentTheme = document.documentElement.getAttribute('data-theme');
    const text = document.getElementById('themeText');

    if (currentTheme === 'dark') {
        text.textContent = 'ライトモード';
    }
});
</script>

ダークモードのカラーをカスタマイズ

独自のダークモードカラーを設定したい場合は、以下のようにCSS変数をオーバーライドします。

<style>
/* ダークモード時のカスタムカラー */
[data-theme="dark"] {
    --color-primary: #60a5fa;
    --color-secondary: #a78bfa;
    --color-success: #34d399;
    --color-danger: #f87171;
    --color-warning: #fbbf24;
    --color-info: #38bdf8;

    --color-bg: #0f172a;
    --color-box: #1e293b;
    --color-text: #f1f5f9;
    --color-border: #334155;
}

/* ライトモード時のカスタムカラー(オプション) */
[data-theme="light"] {
    --color-primary: #3b82f6;
    --color-secondary: #8b5cf6;
    --color-bg: #ffffff;
    --color-text: #1f2937;
}
</style>

トラブルシューティング

Q1: テーマが切り替わらない

解決方法:
  1. HTMLタグに data-theme="light" 属性があるか確認
  2. JavaScriptが正しく読み込まれているか確認
  3. ブラウザのコンソールでエラーがないか確認

Q2: 一部の要素だけダークモードにならない

解決方法:

CSS変数を使用していることを確認してください。

/* BAD: ハードコーディングされた色 */
.my-element {
    background: #ffffff;
    color: #000000;
}

/* GOOD: CSS変数を使用 */
.my-element {
    background: var(--color-bg);
    color: var(--color-text);
}

Q3: ページ読み込み時にテーマが一瞬切り替わる(フラッシュ)

解決方法:

テーマ復元スクリプトを<head>内の最初に配置してください。

<head>
    <script>
        // 最初に実行
        const savedTheme = localStorage.getItem('theme') || 'light';
        document.documentElement.setAttribute('data-theme', savedTheme);
    </script>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="asagiri.min.css">
</head>

ベストプラクティス

推奨事項

  • 常にCSS変数(var(--color-*))を使用する
  • ユーザーの選択をlocalStorageに保存する
  • システム設定を尊重する
  • アクセシビリティを考慮したコントラスト比を保つ
  • 画像にも対応する(ダークモード用の画像を用意)

避けるべきこと

  • ハードコーディングされた色の使用
  • !importantの過度な使用
  • ユーザーの選択を無視してシステム設定を強制
  • コントラスト比の低い配色

実践例:完全なダークモード対応ページ

実際のプロジェクトで使える完全な実装例:

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- テーマフラッシュを防ぐため、最初にテーマを復元 -->
    <script>
        (function() {
            const savedTheme = localStorage.getItem('theme');
            if (savedTheme) {
                document.documentElement.setAttribute('data-theme', savedTheme);
            } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
                document.documentElement.setAttribute('data-theme', 'dark');
            } else {
                document.documentElement.setAttribute('data-theme', 'light');
            }
        })();
    </script>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>完全なダークモード対応</title>
    <link rel="stylesheet" href="dist/asagiri.min.css">

    <style>
        body {
            background-color: var(--color-bg);
            color: var(--color-text);
            transition: background-color 0.3s ease, color 0.3s ease;
        }
    </style>
</head>
<body>
    <!-- ここにコンテンツ -->

    <script src="theme.js"></script>
</body>
</html>

theme.js:

// theme.js - このファイルをコピーして使用できます

class ThemeManager {
    constructor() {
        this.init();
    }

    init() {
        // システム設定の変更を監視
        window.matchMedia('(prefers-color-scheme: dark)')
            .addEventListener('change', (e) => {
                if (!localStorage.getItem('theme')) {
                    this.setTheme(e.matches ? 'dark' : 'light');
                }
            });
    }

    getCurrentTheme() {
        return document.documentElement.getAttribute('data-theme');
    }

    setTheme(theme) {
        document.documentElement.setAttribute('data-theme', theme);
        localStorage.setItem('theme', theme);

        // カスタムイベントを発行
        window.dispatchEvent(new CustomEvent('themechange', {
            detail: { theme }
        }));
    }

    toggleTheme() {
        const currentTheme = this.getCurrentTheme();
        const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
        this.setTheme(newTheme);
        return newTheme;
    }
}

// グローバルインスタンス
const themeManager = new ThemeManager();

// ショートカット関数
function toggleTheme() {
    return themeManager.toggleTheme();
}

// テーマ変更をリッスンする例
window.addEventListener('themechange', (e) => {
    console.log('Theme changed to:', e.detail.theme);
});