ReactでCSS Modulesを使ったダークモードの簡単実装ガイド

Reactアプリケーションで、ユーザー体験を向上させるダークモードの実装は、現代のウェブ開発において欠かせない要素となっています。本記事では、ReactとCSS Modulesを活用して、簡単かつ効率的にダークモードを実現する方法をステップバイステップで解説します。CSS Modulesを使うことで、スタイルのスコープがコンポーネント単位に限定され、メンテナンス性が向上します。これにより、既存のプロジェクトにダークモードを導入する場合でも、他のスタイルに影響を与える心配がありません。初心者から中級者まで、すぐに使える実践的な知識を提供します。

目次

ダークモードの概要と重要性


ダークモードとは、アプリケーションの色調を黒やグレーを基調とした配色に切り替える機能のことです。これにより、視覚的な快適さが向上し、特に暗い環境での画面の眩しさを軽減できます。また、デバイスのバッテリー消費を抑える効果もあるため、ユーザーの満足度や利便性を高める重要な機能とされています。

ダークモードのメリット

  • 視覚疲労の軽減:特に夜間や暗い場所での作業時に目への負担を減らします。
  • エネルギー効率:OLEDやAMOLEDディスプレイを使用したデバイスでは、暗い画面がバッテリー消費を抑えます。
  • トレンドとしての需要:多くのユーザーがダークモード対応を期待しており、アプリの競争力を向上させます。

Reactでのダークモード実装の利点


Reactを使うことで、状態管理や再利用可能なコンポーネントを活用して効率的にダークモードを構築できます。さらに、CSS Modulesを併用することで、スタイルの衝突を回避しながら、テーマごとに異なるデザインを簡単に適用できます。

ReactにおけるCSS Modulesの基本

CSS Modulesは、CSSをコンポーネント単位でスコープ化し、スタイルの競合を防ぐための仕組みです。Reactでは、CSS Modulesを使用することで、各コンポーネントに独立したスタイルを効率的に適用できます。

CSS Modulesの特徴

  • ローカルスコープ: クラス名が一意に生成されるため、他のコンポーネントとスタイルが衝突しません。
  • メンテナンス性の向上: スタイルをコンポーネント単位で管理できるため、コードが整理され、保守が容易になります。
  • 柔軟な統合: CSS Modulesは既存のCSS構文と完全に互換性があり、特別な学習曲線を必要としません。

CSS Modulesの設定方法


ReactプロジェクトでCSS Modulesを利用するには、ファイル名を[name].module.css形式にする必要があります。たとえば、Buttonコンポーネント用のスタイルは以下のように記述します。

/* Button.module.css */
.button {
  background-color: #007bff;
  color: #ffffff;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.button-dark {
  background-color: #333333;
  color: #ffffff;
}

Reactコンポーネントへの適用


以下の例では、ButtonコンポーネントにCSS Modulesを適用します。

// Button.jsx
import React from 'react';
import styles from './Button.module.css';

const Button = ({ isDarkMode, children }) => {
  const buttonStyle = isDarkMode ? styles['button-dark'] : styles.button;
  return <button className={buttonStyle}>{children}</button>;
};

export default Button;

これにより、isDarkModeプロパティに基づいてスタイルが動的に切り替わります。CSS Modulesを活用することで、コンポーネントのスタイルを安全かつ簡潔に管理できます。

ダークモード用のCSS Modulesファイルの作成

ダークモードを実現するためには、ダークテーマ専用のスタイルを記述するCSS Modulesファイルを作成します。このファイルでは、背景色やテキストカラーなど、ダークモード用のプロパティを設定します。

CSS Modulesファイルの作成


以下は、ダークモード用のスタイルを定義したCSS Modulesファイルの例です。

/* DarkMode.module.css */
.container {
  background-color: #121212;
  color: #ffffff;
  padding: 20px;
  border-radius: 10px;
}

.button {
  background-color: #333333;
  color: #ffffff;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.button:hover {
  background-color: #444444;
}

このファイルでは、containerクラスがダークモード全体の背景や文字色を管理し、buttonクラスがボタンのスタイルを定義しています。

ライトモード用のCSS Modulesファイルとの併用


ライトモードのスタイルも同様に定義しておくと、テーマ切り替えがスムーズになります。

/* LightMode.module.css */
.container {
  background-color: #ffffff;
  color: #000000;
  padding: 20px;
  border-radius: 10px;
}

.button {
  background-color: #f0f0f0;
  color: #000000;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.button:hover {
  background-color: #e0e0e0;
}

テーマ別CSSファイルの準備が完了


これで、ダークモード用とライトモード用のスタイルがそれぞれ定義されました。次のステップでは、Reactのコンポーネント内でこれらのスタイルを適用し、動的にテーマを切り替える方法を実装します。

Reactでのテーマ切り替えロジックの実装

Reactでは、テーマ切り替えを動的に行うために、状態管理を活用してロジックを実装します。このセクションでは、useStateフックを用いてダークモードとライトモードの切り替えを実現する方法を解説します。

テーマ切り替えロジックの基本構造


以下のコードは、テーマを切り替えるための基本的なロジックを示した例です。

import React, { useState } from 'react';
import darkStyles from './DarkMode.module.css';
import lightStyles from './LightMode.module.css';

const ThemeToggle = () => {
  const [isDarkMode, setIsDarkMode] = useState(false);

  const toggleTheme = () => {
    setIsDarkMode((prevMode) => !prevMode);
  };

  const styles = isDarkMode ? darkStyles : lightStyles;

  return (
    <div className={styles.container}>
      <h1>{isDarkMode ? 'ダークモード' : 'ライトモード'}</h1>
      <button className={styles.button} onClick={toggleTheme}>
        {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
      </button>
    </div>
  );
};

export default ThemeToggle;

コードのポイント

  1. useStateによる状態管理
  • isDarkModeは現在のテーマ状態を管理するためのフラグとして使用します。
  • 初期値をfalseに設定し、ライトモードをデフォルトとします。
  1. toggleTheme関数
  • ボタンのクリックイベントに紐づけて、テーマを切り替える処理を実行します。
  1. 動的スタイルの適用
  • 現在のテーマ状態に応じて、darkStylesまたはlightStylesを適用します。

結果

  • ライトモードとダークモードの切り替えが、ボタン操作によって即座に反映されます。
  • CSS Modulesのスコープ機能により、スタイルの衝突を気にせず簡単にテーマを変更できます。

次は、このテーマ設定を永続化するための方法について解説します。

ユーザーのテーマ設定を保存する方法

ダークモードの利便性を最大化するためには、ユーザーのテーマ選択を永続化することが重要です。Reactでは、localStorageを活用することで、ユーザーがアプリを再訪した際に、前回選択したテーマを自動的に適用できます。

ローカルストレージを活用した実装


以下のコードは、テーマ設定をlocalStorageに保存し、再読み込み時にその設定を適用する例です。

import React, { useState, useEffect } from 'react';
import darkStyles from './DarkMode.module.css';
import lightStyles from './LightMode.module.css';

const ThemeToggle = () => {
  const [isDarkMode, setIsDarkMode] = useState(() => {
    // 初期状態をlocalStorageから取得
    const savedTheme = localStorage.getItem('isDarkMode');
    return savedTheme === 'true'; // ローカルストレージの値をブール型に変換
  });

  // テーマの変更をlocalStorageに保存
  useEffect(() => {
    localStorage.setItem('isDarkMode', isDarkMode);
  }, [isDarkMode]);

  const toggleTheme = () => {
    setIsDarkMode((prevMode) => !prevMode);
  };

  const styles = isDarkMode ? darkStyles : lightStyles;

  return (
    <div className={styles.container}>
      <h1>{isDarkMode ? 'ダークモード' : 'ライトモード'}</h1>
      <button className={styles.button} onClick={toggleTheme}>
        {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
      </button>
    </div>
  );
};

export default ThemeToggle;

コードのポイント

  1. 初期値の取得
  • useStateの初期値として、localStorageに保存されたテーマ設定を取得します。
  • 未保存の場合はデフォルト値(ライトモード)を設定します。
  1. useEffectによる保存
  • isDarkModeの状態が変更されるたびに、localStorageへ現在のテーマ設定を保存します。
  1. データ型の変換
  • localStorageは文字列形式でデータを保存するため、テーマ状態を文字列'true'または'false'として保存し、取得時にブール値に変換します。

結果

  • ユーザーが選択したテーマは、ページをリロードしても保持されます。
  • 他のデバイスで同じテーマを再設定する必要がなく、使い勝手が向上します。

次のセクションでは、保存されたテーマ設定を基にReactコンポーネントにスタイルを適用する方法をさらに掘り下げます。

コンポーネントにダークモードを適用する

ダークモードをReactコンポーネントに適用するには、動的にスタイルを切り替える仕組みを実装します。ここでは、CSS Modulesを活用して、各コンポーネントにテーマを適用する方法を解説します。

テーマに応じたスタイルの適用


以下の例では、テーマの状態をコンポーネントにプロパティとして渡し、スタイルを動的に変更します。

// ThemeToggle.jsx
import React, { useState, useEffect } from 'react';
import darkStyles from './DarkMode.module.css';
import lightStyles from './LightMode.module.css';
import Card from './Card';

const ThemeToggle = () => {
  const [isDarkMode, setIsDarkMode] = useState(() => {
    const savedTheme = localStorage.getItem('isDarkMode');
    return savedTheme === 'true';
  });

  useEffect(() => {
    localStorage.setItem('isDarkMode', isDarkMode);
  }, [isDarkMode]);

  const toggleTheme = () => {
    setIsDarkMode((prevMode) => !prevMode);
  };

  const styles = isDarkMode ? darkStyles : lightStyles;

  return (
    <div className={styles.container}>
      <h1>{isDarkMode ? 'ダークモード' : 'ライトモード'}</h1>
      <button className={styles.button} onClick={toggleTheme}>
        {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
      </button>
      <Card isDarkMode={isDarkMode} />
    </div>
  );
};

export default ThemeToggle;

個別コンポーネントでのスタイル適用


次に、Cardコンポーネントでテーマに応じたスタイルを適用します。

// Card.jsx
import React from 'react';
import darkStyles from './DarkMode.module.css';
import lightStyles from './LightMode.module.css';

const Card = ({ isDarkMode }) => {
  const styles = isDarkMode ? darkStyles : lightStyles;

  return (
    <div className={styles.card}>
      <h2>テーマ適用カード</h2>
      <p>{isDarkMode ? 'ダークモードで表示中' : 'ライトモードで表示中'}</p>
    </div>
  );
};

export default Card;

スタイルファイルの例


DarkMode.module.cssLightMode.module.cssに以下のスタイルを追加します。

/* DarkMode.module.css */
.card {
  background-color: #1e1e1e;
  color: #ffffff;
  padding: 20px;
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
/* LightMode.module.css */
.card {
  background-color: #ffffff;
  color: #000000;
  padding: 20px;
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

結果

  • Cardコンポーネントのスタイルが、テーマに応じて動的に切り替わります。
  • 複数のコンポーネントで同様の方法を使用し、テーマ適用を一貫して管理できます。

次は、テーマ適用中に発生する可能性のある問題を解決する方法を解説します。

ダークモードのトラブルシューティング

ダークモードを実装する際、予期せぬエラーや問題が発生する場合があります。このセクションでは、よくある問題とその解決方法について解説します。

1. スタイルが適用されない

問題の原因

  • CSS Modulesのファイル名が間違っている (.module.css が必要)。
  • インポートが正しくない、またはクラス名が一致していない。

解決方法

  • CSSファイル名を確認し、必ず[name].module.css形式になっていることを確認します。
  • クラス名がCSSファイルとReactコードで一致していることを確認します。
// 正しいインポート例
import styles from './DarkMode.module.css';
<div className={styles.container}></div>;

2. テーマがリロード後に元に戻る

問題の原因

  • localStorageにテーマ設定が正しく保存されていない。
  • 初期値としてlocalStorageから値を読み取っていない。

解決方法

  • 初期状態をlocalStorageから取得し、useStateの初期値として設定します。
const [isDarkMode, setIsDarkMode] = useState(() => {
  const savedTheme = localStorage.getItem('isDarkMode');
  return savedTheme === 'true';
});
  • 設定変更時にlocalStorageへの保存処理が正しく行われていることを確認します。

3. CSS Modulesのスコープが適用されない

問題の原因

  • CSS Modulesが構成されていない、または開発環境の設定に問題がある。

解決方法

  • Create React Appを使用している場合、CSS Modulesはデフォルトで有効です。
  • カスタム環境の場合、Webpackの設定を確認し、css-loadermodulesオプションを有効にします。
// Webpack設定例
{
  test: /\.module\.css$/,
  use: ['style-loader', 'css-loader?modules']
}

4. ボタンのクリックでテーマが切り替わらない

問題の原因

  • toggleTheme関数が正しく実装されていないか、イベントハンドラーがボタンに紐づいていない。

解決方法

  • ボタンにonClickイベントを設定し、toggleTheme関数が正しく呼び出されていることを確認します。
<button onClick={toggleTheme}>
  {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
</button>

5. カスタムフォントや画像がテーマ変更に対応しない

問題の原因

  • フォントや画像がCSSファイルで明示的に設定されていない。

解決方法

  • テーマごとのCSSファイルにフォントや画像の設定を追加します。
/* DarkMode.module.css */
.container {
  background-image: url('/path/to/dark-background.png');
  font-family: 'Arial, sans-serif';
}

結果


これらの解決策を適用することで、ダークモード実装中のほとんどの問題を解消できます。次は、ダークモードとライトモードをスムーズに切り替えるためのアニメーションを追加する方法を解説します。

応用例:アニメーションを使ったスムーズなテーマ切り替え

テーマを切り替える際、視覚的な滑らかさを追加することで、より良いユーザー体験を提供できます。このセクションでは、CSSアニメーションを使って、ダークモードとライトモードの切り替えをスムーズにする方法を解説します。

CSSアニメーションの設定


まず、CSS Modulesファイルにアニメーションを定義します。

/* DarkMode.module.css */
@keyframes fadeInDark {
  from {
    background-color: #ffffff;
    color: #000000;
  }
  to {
    background-color: #121212;
    color: #ffffff;
  }
}

.container {
  animation: fadeInDark 0.5s ease-in-out;
  background-color: #121212;
  color: #ffffff;
  padding: 20px;
  border-radius: 10px;
}
/* LightMode.module.css */
@keyframes fadeInLight {
  from {
    background-color: #121212;
    color: #ffffff;
  }
  to {
    background-color: #ffffff;
    color: #000000;
  }
}

.container {
  animation: fadeInLight 0.5s ease-in-out;
  background-color: #ffffff;
  color: #000000;
  padding: 20px;
  border-radius: 10px;
}

このアニメーションは、テーマ切り替え時に背景色と文字色が滑らかに変更される効果を持っています。

Reactコンポーネントへの適用


アニメーションを活用したテーマ切り替えを、ThemeToggleコンポーネントで実装します。

// ThemeToggle.jsx
import React, { useState, useEffect } from 'react';
import darkStyles from './DarkMode.module.css';
import lightStyles from './LightMode.module.css';

const ThemeToggle = () => {
  const [isDarkMode, setIsDarkMode] = useState(() => {
    const savedTheme = localStorage.getItem('isDarkMode');
    return savedTheme === 'true';
  });

  useEffect(() => {
    localStorage.setItem('isDarkMode', isDarkMode);
  }, [isDarkMode]);

  const toggleTheme = () => {
    setIsDarkMode((prevMode) => !prevMode);
  };

  const styles = isDarkMode ? darkStyles : lightStyles;

  return (
    <div className={styles.container}>
      <h1>{isDarkMode ? 'ダークモード' : 'ライトモード'}</h1>
      <button className={styles.button} onClick={toggleTheme}>
        {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'}
      </button>
    </div>
  );
};

export default ThemeToggle;

テーマ切り替え時のアニメーションの仕組み

  • styles.containerでアニメーションを指定しているため、テーマ変更時にアニメーションがトリガーされます。
  • @keyframesで定義した変化(背景色や文字色の変更)が0.5秒の間に滑らかに実行されます。

結果

  • テーマ切り替えが視覚的に心地よくなり、アプリ全体のクオリティが向上します。
  • アニメーション時間や効果を調整することで、アプリのデザインに最適な切り替え効果をカスタマイズ可能です。

次は、この記事の内容を簡潔にまとめます。

まとめ

本記事では、ReactでCSS Modulesを活用したダークモードの実装方法を解説しました。ダークモードの重要性から始まり、CSS Modulesの基本、テーマ切り替えロジックの構築、設定の永続化、コンポーネントへの適用、さらにはアニメーションを使った滑らかな切り替えまで、ステップバイステップで紹介しました。これにより、視覚的に快適で使いやすいダークモードを簡単に実現できるようになります。適切なトラブルシューティングと応用例を活用し、あなたのアプリに最適なダークモードをぜひ実装してみてください。

コメント

コメントする

目次