Back

CSSとJavaScriptでダークモードトグルを構築する方法

CSSとJavaScriptでダークモードトグルを構築する方法

ダークモードは、現代のウェブサイトにとって不可欠な機能となっています。ユーザーは、環境、時間帯、または個人の好みに応じてテーマを切り替えることを期待しています。このチュートリアルでは、システム設定を尊重し、ユーザーの選択を記憶し、最適なパフォーマンスのために最新のCSS機能を活用する、堅牢なダークモードトグルの構築方法を紹介します。

重要なポイント

  • システム設定を尊重し、ユーザーの選択を保持するダークモードトグルを構築
  • 効率的なテーマ設定のためにCSSカスタムプロパティとcolor-schemeプロパティを使用
  • ページ読み込み時のスタイル未適用コンテンツのフラッシュを防ぐJavaScriptを実装
  • キーボードナビゲーションとスクリーンリーダーのアクセシビリティベストプラクティスを適用

HTML構造のセットアップ

トグルボタンを含み、初期テーマを宣言するセマンティックHTMLから始めます:

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="color-scheme" content="light dark">
    <title>Dark Mode Toggle Demo</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <button type="button" id="theme-toggle" aria-label="Toggle dark mode">
        <span class="toggle-text">Dark</span>
    </button>
    <!-- Your content here -->
</body>
</html>

HTML要素のdata-theme属性がテーマを制御します。color-schemeメタタグは、スクロールバーやフォームコントロールなどのネイティブUI要素を適応させるようブラウザに指示します。

最新機能を使用したCSSの実装

CSSカスタムプロパティとcolor-schemeの使用

CSSカスタムプロパティを使用してカラートークンを定義し、ネイティブ要素のテーマ設定のためにCSS color-schemeプロパティを活用します:

:root {
    color-scheme: light dark;
    
    /* Light theme colors (default) */
    --bg-color: #ffffff;
    --text-color: #213547;
    --accent-color: #0066cc;
}

[data-theme="dark"] {
    --bg-color: #1a1a1a;
    --text-color: #e0e0e0;
    --accent-color: #66b3ff;
}

body {
    background-color: var(--bg-color);
    color: var(--text-color);
    transition: background-color 0.3s ease, color 0.3s ease;
}

prefers-color-schemeでシステム設定を尊重

prefers-color-schemeのサポートを追加して、システム設定に自動的に合わせます:

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg-color: #1a1a1a;
        --text-color: #e0e0e0;
        --accent-color: #66b3ff;
    }
}

最新のlight-dark()関数

light-dark()関数をサポートするブラウザでは、カラー定義を簡素化できます:

:root {
    color-scheme: light dark;
}

body {
    background-color: light-dark(#ffffff, #1a1a1a);
    color: light-dark(#213547, #e0e0e0);
}

この関数は、アクティブなカラースキームに基づいて適切な色を自動的に選択し、コードの重複を削減します。

トグルと永続化のためのJavaScript

読み込み時のテーマフラッシュの防止

スタイル未適用コンテンツのフラッシュを防ぐため、このスクリプトを<body>開始タグの直後に配置します:

<script>
    // Apply saved theme immediately to prevent flash
    (function() {
        const saved = localStorage.getItem('theme');
        if (saved) {
            document.documentElement.setAttribute('data-theme', saved);
        } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
            document.documentElement.setAttribute('data-theme', 'dark');
        }
    })();
</script>

完全なダークモード実装

トグル機能のために、このJavaScriptを追加します:

const toggle = document.getElementById('theme-toggle');
const html = document.documentElement;

// Get current theme
function getTheme() {
    return html.getAttribute('data-theme') || 
           (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
}

// Set theme and update UI
function setTheme(theme) {
    html.setAttribute('data-theme', theme);
    localStorage.setItem('theme', theme);
    updateToggleText(theme);
}

// Update button text
function updateToggleText(theme) {
    const text = toggle.querySelector('.toggle-text');
    text.textContent = theme === 'dark' ? 'Light' : 'Dark';
    toggle.setAttribute('aria-label', `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`);
}

// Initialize
updateToggleText(getTheme());

// Handle toggle click
toggle.addEventListener('click', () => {
    const current = getTheme();
    setTheme(current === 'dark' ? 'light' : 'dark');
});

// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
    if (!localStorage.getItem('theme')) {
        setTheme(e.matches ? 'dark' : 'light');
    }
});

アクセシビリティとベストプラクティス

必須のアクセシビリティ機能

ダークモードトグルは、キーボードでアクセス可能で、適切にラベル付けされている必要があります:

#theme-toggle:focus-visible {
    outline: 2px solid var(--accent-color);
    outline-offset: 2px;
}

両方のテーマで十分な色のコントラストを確保してください。ダークモードは低コントラストを意味するものではありません。少なくともWCAG AA基準(通常のテキストで4.5:1)を維持してください。

避けるべきこと

ページ全体にfilter: invert(1)を使用しないでください。画像やメディアが不必要に反転されます。ダークモードで純粋な黒(#000000)の背景を避けてください。読みやすさを向上させるために、ダークグレー(#1a1a1a)を使用してください。CSSフォールバックなしでJavaScriptのみに依存しないでください。

実装のテスト

さまざまなシナリオでダークモードトグルをテストします:

  1. localStorageをクリアして、システム設定の検出が機能することを確認
  2. テーマを切り替えて更新し、永続化を確認
  3. サイトが開いている間にシステム設定を変更
  4. ブラウザのDevToolsを使用してコントラスト比を確認
  5. キーボードナビゲーションとスクリーンリーダーのアナウンスをテスト

まとめ

適切に実装されたダークモードソリューションは、ユーザーの好みを尊重し、選択を保持し、最新のCSS機能を活用します。color-schemeプロパティ、カスタムプロパティ、最小限のJavaScriptを組み合わせることで、パフォーマンスが高く、アクセシブルで、ユーザーフレンドリーなテーマスイッチャーを作成できます。light-dark()関数は最新のブラウザのカラー管理を簡素化し、フォールバックは互換性を保証します。

覚えておいてください:最良のダークモード実装は、ユーザーには見えないものです。ユーザーが期待する通りに機能するだけです。

よくある質問

はい、prefers-color-schemeを使用したCSSメディアクエリでシステム設定を検出できます。ただし、ユーザーの好みを保存し、システム設定を上書きする手動トグルボタンを提供するにはJavaScriptが必要です。

これは、ページのレンダリング後にJavaScriptが実行される場合に発生します。このフラッシュを防ぐには、bodyタグの直後にlocalStorageをチェックし、ページの描画前に保存されたテーマを適用するスクリプトを配置してください。

いいえ、純粋な黒は目の疲労を引き起こし、テキストを読みにくくする可能性があります。代わりに、#1a1a1aや#121212のようなダークグレーを使用してください。これらの色は疲労を軽減しながら、アクセシビリティのための良好なコントラスト比を維持します。

画像にfilter invertを使用しないでください。代わりに、ダークモード用の代替バージョンの画像を提供するか、両方の背景で機能する色の透明PNGを使用してください。複雑なグラフィックの場合は、CSSマスクやcurrentColorを使用したSVGの使用を検討してください。

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay