3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSSAdvent Calendar 2024

Day 11

CSSでダークモード/ライトモードの切り替えを実装する

Posted at

はじめに

最近のWebアプリケーションでは、ユーザーの好みに応じてダークモードとライトモードを切り替える機能が一般的になってきています。この記事では、CSSのカスタムプロパティ(CSS変数)を使用して、シンプルかつ効果的なテーマ切り替え機能を実装する方法をご紹介します。

image.png

成果物のイメージ

ボタンによりテーマ切り替える。

image.png

image.png

実装のポイント

  • CSS変数を使用してテーマカラーを管理
  • レスポンシブデザインへの対応
  • アニメーションによる視覚的な演出
  • JavaScriptによる動的なテーマ切り替え

実装手順

1. プロジェクトの構成

まず、以下のような構成でファイルを作成します:

output/
  ├── dark-theme.css
  ├── light-theme.css
  ├── responsive.css
  ├── animation.css
  └── index.html

2. テーマごとのCSS定義

ダークテーマ(dark-theme.css)

:root {
    --background-color: #121212;
    --text-color: #ffffff;
    --primary-color: #BB86FC;
    --secondary-color: #03DAC6;
}

body {
    background-color: var(--background-color);
    color: var(--text-color);
}

.card {
    background-color: #1E1E1E;
    border: 1px solid #333333;
}

ライトテーマ(light-theme.css)

:root {
    --background-color: #ffffff;
    --text-color: #333333;
    --primary-color: #6200EE;
    --secondary-color: #03DAC6;
}

body {
    background-color: var(--background-color);
    color: var(--text-color);
}

.card {
    background-color: #f5f5f5;
    border: 1px solid #e0e0e0;
}

3. レスポンシブデザインの実装(responsive.css)

html {
    font-size: 16px;
}

h1 {
    font-size: 2.5em;
}

@media (max-width: 768px) {
    html {
        font-size: 14px;
    }
    h1 {
        font-size: 2em;
    }
}

4. アニメーションの追加(animation.css)

@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.fade-in {
    animation: fadeIn 1.5s ease-in-out;
}

5. HTMLの実装

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSSテーマ確認</title>
    <link id="theme-link" rel="stylesheet" href="output/dark-theme.css">
    <link rel="stylesheet" href="output/responsive.css">
    <link rel="stylesheet" href="output/animation.css">
    <style>
        .switch-buttons {
            margin: 20px;
        }
        button {
            margin: 5px;
            padding: 10px 20px;
            cursor: pointer;
        }
        .card {
            margin: 20px auto;
            padding: 30px;
            width: 80%;
            max-width: 500px;
            text-align: center;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <div class="switch-buttons">
        <button onclick="switchTheme('dark')">ダークテーマ</button>
        <button onclick="switchTheme('light')">ライトテーマ</button>
    </div>
    <h1 class="fade-in">CSSテーマ確認ページ</h1>
    <div class="card fade-in">
        カードデザインのサンプルです。
    </div>
    <script>
        function switchTheme(theme) {
            const link = document.getElementById('theme-link');
            if (theme === 'dark') {
                link.href = 'output/dark-theme.css';
            } else {
                link.href = 'output/light-theme.css';
            }
        }
    </script>
</body>
</html>

6. Pythonによるファイル生成

以下のPythonスクリプトを使用して、必要なファイルを自動生成します:

import os

os.makedirs('output', exist_ok=True)

# CSSファイルの内容
dark_css = '''
:root {
    --background-color: #121212;
    --text-color: #ffffff;
    --primary-color: #BB86FC;
    --secondary-color: #03DAC6;
}
body {
    background-color: var(--background-color);
    color: var(--text-color);
}
.card {
    background-color: #1E1E1E;
    border: 1px solid #333333;
}
'''

light_css = '''
:root {
    --background-color: #ffffff;
    --text-color: #333333;
    --primary-color: #6200EE;
    --secondary-color: #03DAC6;
}
body {
    background-color: var(--background-color);
    color: var(--text-color);
}
.card {
    background-color: #f5f5f5;
    border: 1px solid #e0e0e0;
}
'''

responsive_css = '''
html {
    font-size: 16px;
}
h1 {
    font-size: 2.5em;
}
@media (max-width: 768px) {
    html {
        font-size: 14px;
    }
    h1 {
        font-size: 2em;
    }
}
'''

animation_css = '''
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
.fade-in {
    animation: fadeIn 1.5s ease-in-out;
}
'''

# HTMLの内容
html_content = '''
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSSテーマ確認</title>
    <link id="theme-link" rel="stylesheet" href="output/dark-theme.css">
    <link rel="stylesheet" href="output/responsive.css">
    <link rel="stylesheet" href="output/animation.css">
    <style>
        .switch-buttons {
            margin: 20px;
        }
        button {
            margin: 5px;
            padding: 10px 20px;
            cursor: pointer;
        }
        .card {
            margin: 20px auto;
            padding: 30px;
            width: 80%;
            max-width: 500px;
            text-align: center;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <div class="switch-buttons">
        <button onclick="switchTheme('dark')">ダークテーマ</button>
        <button onclick="switchTheme('light')">ライトテーマ</button>
    </div>
    <h1 class="fade-in">CSSテーマ確認ページ</h1>
    <div class="card fade-in">
        カードデザインのサンプルです。
    </div>

    <script>
        function switchTheme(theme) {
            const link = document.getElementById('theme-link');
            if (theme === 'dark') {
                link.href = 'output/dark-theme.css';
            } else {
                link.href = 'output/light-theme.css';
            }
        }
    </script>
</body>
</html>
'''

# ファイルを出力
with open('output/dark-theme.css', 'w') as f:
    f.write(dark_css)

with open('output/light-theme.css', 'w') as f:
    f.write(light_css)

with open('output/responsive.css', 'w') as f:
    f.write(responsive_css)

with open('output/animation.css', 'w') as f:
    f.write(animation_css)

with open('output/index.html', 'w') as f:
    f.write(html_content)

print("CSSとHTMLファイルを生成しました。")

実装のポイント解説

image.png

CSS変数の活用

テーマカラーをCSS変数として定義することで、以下のメリットがあります:

  1. 色の一元管理が可能
  2. 変数の再利用が容易
  3. JavaScriptからの操作も可能

レスポンシブデザイン

  • メディアクエリを使用して画面サイズに応じたスタイル調整
  • フォントサイズをemで指定し、相対的なサイズ調整を実現

アニメーション

  • @keyframesを使用したフェードインアニメーション
  • ページの読み込み時やテーマ切り替え時の視覚的な演出

テーマ切り替えの実装

JavaScriptを使用してlink要素のhref属性を動的に切り替えることで、テーマの切り替えを実現しています。

まとめ

image.png

この実装方法のメリットは以下の通りです:

  1. シンプルで理解しやすい構造
  2. CSS変数による効率的なテーマ管理
  3. スムーズなアニメーション効果
  4. レスポンシブ対応による優れたユーザー体験

今回の実装は基本的な例ですが、これをベースに以下のような拡張が可能です:

  • ユーザーの設定を保存(LocalStorage使用)
  • システムのテーマ設定との連動
  • より複雑なアニメーション効果の追加

参考資料

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?