実験的なCSS light-dark()関数を試してみる

## はじめに

⚠️ **重要な注意点**
`light-dark()` 関数は現在**実験的な機能**です。この記事は学習と実験を目的としており、本番環境での使用は推奨していません。

ダークモードは現代のWebアプリケーションにおいて重要な機能の一つとなっています。この記事では、実験的な機能である CSS の `light-dark()` 関数を試してみて、その可能性と現状の制限について探ってみましょう。

https://qiita.com/Tadataka_Takahashi/items/32e8c133da28068c4cf1

上記の記事にたいして、この関数の存在をコメントいただいたので確認してみました。

## light-dark()関数とは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/dec0d8fe-0af2-392f-93a5-e5c132d4a9c7.png)

`light-dark()` は実験的なCSS関数で、色の設定に関して以下の2つのケースを自動的に検出して適用することができます：

1. 開発者が明色/暗色カラースキームを設定した場合
2. ユーザーがライト/ダークテーマをリクエストした場合

基本的な構文：
```css
light-dark(<light-value>, <dark-value>)
```

- 第1引数：ライトモードで使用する値
- 第2引数：ダークモードで使用する値

## 試してみよう

### 成果物のイメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/3cf3d4ba-3db7-38c7-36d4-b8466aec2d9f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/c1be9cfb-10c3-3431-fb89-4b37fbb52b48.png)

### サンプルコード

#### HTML
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>light-dark()の実験</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="switch-buttons">
        <button onclick="switchTheme()">テーマ切り替え</button>
    </div>
    <h1>light-dark()の実験</h1>
    <div class="card">
        カードのサンプルです。
    </div>
    <div class="warning">
        ⚠️ この実装は実験的な機能を使用しています
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const savedTheme = localStorage.getItem('theme');
            if (savedTheme === 'dark') {
                document.documentElement.setAttribute('color-scheme', 'dark');
            }
        });

        function switchTheme() {
            const root = document.documentElement;
            const currentScheme = root.getAttribute('color-scheme');
            const newScheme = currentScheme === 'dark' ? 'light' : 'dark';
            root.setAttribute('color-scheme', newScheme);
            localStorage.setItem('theme', newScheme);
        }
    </script>
</body>
</html>
```

#### CSS
```css
/* 実験的な機能を使用する実装 */
:root {
    color-scheme: light;
}

:root[color-scheme="dark"] {
    color-scheme: dark;
}

body {
    background-color: light-dark(#ffffff, #121212);
    color: light-dark(#333333, #ffffff);
    transition: all 0.3s ease;
    min-height: 100vh;
    margin: 0;
    padding: 20px;
}

.card {
    background-color: light-dark(#f5f5f5, #1e1e1e);
    border: 1px solid light-dark(#e0e0e0, #333333);
    padding: 20px;
    margin: 20px 0;
    border-radius: 8px;
    transition: all 0.3s ease;
}

.warning {
    background-color: light-dark(#fff3cd, #332b1f);
    color: light-dark(#664d03, #ffb86c);
    padding: 1rem;
    border-radius: 4px;
    margin: 20px 0;
}

/* その他のスタイルは省略 */
```

## light-dark()関数を試す価値がある理由

実験的ではありますが、以下の理由で試してみる価値があります：

1. **将来性**
   - CSS Color Module Level 4の一部として標準化が進められている
   - よりエレガントなダークモード実装の可能性

2. **学習機会**
   - 新しいCSS機能の動作を理解する
   - フォールバックの実装方法を学ぶ

3. **実験プロジェクトに最適**
   - プロトタイプの作成
   - 個人プロジェクトでの実験

## まとめ

`light-dark()` 関数は興味深い実験的機能ですが、現時点では以下の点に注意が必要です：

- 実験的機能であり、本番環境での使用は推奨されない
- ブラウザサポートが限定的
- 必要に応じてフォールバックの実装を検討

しかし、将来的な可能性を探るため、以下の環境で試してみることをお勧めします：
- 実験的なプロジェクト
- プロトタイプ
- 学習環境

## 参考情報

- [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#funcdef-light-dark)
- [MDN: light-dark()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark)
- [Can I use: CSS light-dark()](https://caniuse.com/?search=light-dark())

## 検証用のファイルを出力するpython

```python
import os

# 出力ディレクトリの作成
output_dir = 'light-dark-test'
os.makedirs(output_dir, exist_ok=True)

# 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>light-dark()の実験</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="switch-buttons">
        <button onclick="switchTheme()">テーマ切り替え</button>
    </div>
    <h1>light-dark()の実験</h1>
    <div class="card">
        カードのサンプルです。
    </div>
    <div class="warning">
        ⚠️ この実装は実験的な機能を使用しています
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const savedTheme = localStorage.getItem('theme');
            if (savedTheme === 'dark') {
                document.documentElement.setAttribute('color-scheme', 'dark');
            }
        });

        function switchTheme() {
            const root = document.documentElement;
            const currentScheme = root.getAttribute('color-scheme');
            const newScheme = currentScheme === 'dark' ? 'light' : 'dark';
            root.setAttribute('color-scheme', newScheme);
            localStorage.setItem('theme', newScheme);
        }
    </script>
</body>
</html>'''

# CSS（light-dark関数を使用）
css_content = '''/* 実験的な機能を使用する実装 */
:root {
    color-scheme: light;
}

:root[color-scheme="dark"] {
    color-scheme: dark;
}

body {
    background-color: light-dark(#ffffff, #121212);
    color: light-dark(#333333, #ffffff);
    transition: all 0.3s ease;
    min-height: 100vh;
    margin: 0;
    padding: 20px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

.card {
    background-color: light-dark(#f5f5f5, #1e1e1e);
    border: 1px solid light-dark(#e0e0e0, #333333);
    padding: 20px;
    margin: 20px 0;
    border-radius: 8px;
    transition: all 0.3s ease;
}

.warning {
    background-color: light-dark(#fff3cd, #332b1f);
    color: light-dark(#664d03, #ffb86c);
    padding: 1rem;
    border-radius: 4px;
    margin: 20px 0;
}

.switch-buttons {
    margin: 20px 0;
}

button {
    padding: 10px 20px;
    border-radius: 4px;
    border: 1px solid light-dark(#e0e0e0, #333333);
    background-color: light-dark(#f5f5f5, #1e1e1e);
    color: light-dark(#333333, #ffffff);
    cursor: pointer;
    transition: all 0.3s ease;
}

button:hover {
    opacity: 0.9;
}

h1 {
    color: light-dark(#1a73e8, #77b6ff);
    margin-bottom: 2rem;
}'''

# フォールバック用のCSS
fallback_css_content = '''/* フォールバック実装 */
:root {
    /* ライトモードのデフォルト値 */
    --bg-color: #ffffff;
    --text-color: #333333;
    --card-bg: #f5f5f5;
    --border-color: #e0e0e0;
    --warning-bg: #fff3cd;
    --warning-text: #664d03;
    --heading-color: #1a73e8;
}

:root[color-scheme="dark"] {
    /* ダークモードの値 */
    --bg-color: #121212;
    --text-color: #ffffff;
    --card-bg: #1e1e1e;
    --border-color: #333333;
    --warning-bg: #332b1f;
    --warning-text: #ffb86c;
    --heading-color: #77b6ff;
}

body {
    /* フォールバック */
    background-color: var(--bg-color);
    color: var(--text-color);
    /* 実験的機能 */
    background-color: light-dark(#ffffff, #121212);
    color: light-dark(#333333, #ffffff);
    
    transition: all 0.3s ease;
    min-height: 100vh;
    margin: 0;
    padding: 20px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* 以下同様にフォールバックと実験的機能を併記 */'''

# READMEファイル
readme_content = '''# light-dark()関数の実験

このプロジェクトは CSS の実験的な `light-dark()` 関数を試すためのサンプルです。

## 注意事項
- この実装は実験的な機能を使用しています
- 最新のブラウザでのみ動作します
- 学習・実験目的での使用を想定しています

## ファイル構成
- `index.html`: メインのHTMLファイル
- `style.css`: light-dark()関数を使用したCSS
- `style-fallback.css`: フォールバック実装を含むCSS（参考用）

## 使用方法
1. ファイルをWebサーバーで配信するか、ローカルで開く
2. 「テーマ切り替え」ボタンでライト/ダークモードを切り替え
3. 設定は localStorage に保存されます

## 参考情報
- [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#funcdef-light-dark)
- [MDN: light-dark()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark)
'''

# ファイルの出力
def write_file(filename, content):
    filepath = os.path.join(output_dir, filename)
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(content)

# 各ファイルを生成
write_file('index.html', html_content)
write_file('style.css', css_content)
write_file('style-fallback.css', fallback_css_content)
write_file('README.md', readme_content)

print(f"ファイルが {output_dir} ディレクトリに生成されました。")
print("生成されたファイル:")
print("- index.html")
print("- style.css")
print("- style-fallback.css")
print("- README.md")
```