logo
eng-flag

Introduction

Dark mode has become a popular feature in modern web applications, offering users the ability to switch between light and dark color schemes. This guide will walk you through implementing a dark mode toggle in a React application, covering various aspects and best practices.

Table of Contents

  1. Setting Up the Project
  2. Creating the Dark Mode Context
  3. Implementing the Toggle Component
  4. Applying Dark Mode Styles
  5. Persisting User Preference
  6. Handling System Preferences
  7. Optimizing Performance
  8. Accessibility Considerations
  9. Testing Dark Mode
  10. Advanced Techniques

Setting Up the Project

First, let's create a new React project using Create React App:

npx create-react-app dark-mode-toggle
cd dark-mode-toggle
npm start

Creating the Dark Mode Context

To manage the dark mode state across your application, we'll use React Context:

// src/contexts/DarkModeContext.js
import React, { createContext, useState, useContext, useEffect } from 'react';

const DarkModeContext = createContext();

export const DarkModeProvider = ({ children }) => {
  const [isDarkMode, setIsDarkMode] = useState(false);

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

  useEffect(() => {
    const isDark = localStorage.getItem('darkMode') === 'true';
    setIsDarkMode(isDark);
  }, []);

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

  return (
    <DarkModeContext.Provider value={{ isDarkMode, toggleDarkMode }}>
      {children}
    </DarkModeContext.Provider>
  );
};

export const useDarkMode = () => useContext(DarkModeContext);

Implementing the Toggle Component

Now, let's create a toggle component:

// src/components/DarkModeToggle.js
import React from 'react';
import { useDarkMode } from '../contexts/DarkModeContext';

const DarkModeToggle = () => {
  const { isDarkMode, toggleDarkMode } = useDarkMode();

  return (
    <button onClick={toggleDarkMode}>
      {isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
    </button>
  );
};

export default DarkModeToggle;

Applying Dark Mode Styles

To apply dark mode styles, we can use CSS variables:

/* src/styles/darkMode.css */
:root {
  --background-color: #ffffff;
  --text-color: #000000;
}

[data-theme='dark'] {
  --background-color: #1a1a1a;
  --text-color: #ffffff;
}

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

Update your App component to apply the theme:

// src/App.js
import React from 'react';
import { useDarkMode } from './contexts/DarkModeContext';
import DarkModeToggle from './components/DarkModeToggle';
import './styles/darkMode.css';

function App() {
  const { isDarkMode } = useDarkMode();

  return (
    <div data-theme={isDarkMode ? 'dark' : 'light'}>
      <h1>Dark Mode Toggle Example</h1>
      <DarkModeToggle />
      {/* Your app content */}
    </div>
  );
}

export default App;

Persisting User Preference

We've already implemented persistence in our DarkModeContext using localStorage. This ensures that the user's preference is remembered across sessions.

Handling System Preferences

To respect the user's system preferences, we can use the prefers-color-scheme media query:

// Update DarkModeContext.js
useEffect(() => {
  const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
  const handleChange = () => setIsDarkMode(mediaQuery.matches);
  
  mediaQuery.addListener(handleChange);
  setIsDarkMode(mediaQuery.matches);

  return () => mediaQuery.removeListener(handleChange);
}, []);

Optimizing Performance

To optimize performance, consider using the useCallback hook for the toggleDarkMode function:

const toggleDarkMode = useCallback(() => {
  setIsDarkMode(prevMode => !prevMode);
}, []);

Accessibility Considerations

Ensure your toggle component is keyboard accessible and has proper ARIA attributes:

// Update DarkModeToggle.js
return (
  <button
    onClick={toggleDarkMode}
    aria-pressed={isDarkMode}
    aria-label="Toggle dark mode"
  >
    {isDarkMode ? '🌙' : '☀️'}
  </button>
);

Testing Dark Mode

Here's an example of how to test your dark mode implementation:

// src/__tests__/DarkModeToggle.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { DarkModeProvider } from '../contexts/DarkModeContext';
import DarkModeToggle from '../components/DarkModeToggle';

test('toggles dark mode when clicked', () => {
  const { getByRole } = render(
    <DarkModeProvider>
      <DarkModeToggle />
    </DarkModeProvider>
  );

  const toggle = getByRole('button');
  expect(toggle).toHaveTextContent('Switch to Dark Mode');

  fireEvent.click(toggle);
  expect(toggle).toHaveTextContent('Switch to Light Mode');
});

Advanced Techniques

Themed Components

You can create themed components that adapt to the current mode:

// src/components/ThemedButton.js
import React from 'react';
import styled from 'styled-components';
import { useDarkMode } from '../contexts/DarkModeContext';

const StyledButton = styled.button`
  background-color: e=>e.isDarkMode?"#333":"#f0f0f0";
  color: e=>e.isDarkMode?"#fff":"#000";
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
`;

const ThemedButton = ({ children, ...props }) => {
  const { isDarkMode } = useDarkMode();
  return <StyledButton isDarkMode={isDarkMode} {...props}>{children}</StyledButton>;
};

export default ThemedButton;

Animated Transitions

To create smooth transitions between modes, you can use CSS transitions:

/* Update darkMode.css */
body {
  transition: background-color 0.5s ease, color 0.5s ease;
}

[data-theme='dark'] .animate-dark-mode {
  animation: toDark 0.5s ease forwards;
}

[data-theme='light'] .animate-dark-mode {
  animation: toLight 0.5s ease forwards;
}

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

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

Dynamic Theming

For more complex theming, you can create a theme object and use it with styled-components:

// src/themes/index.js
export const lightTheme = {
  body: '#fff',
  text: '#000',
  primary: '#0077ff',
  secondary: '#ff4081',
};

export const darkTheme = {
  body: '#1a1a1a',
  text: '#fff',
  primary: '#00aaff',
  secondary: '#ff6b9b',
};

// src/App.js
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { useDarkMode } from './contexts/DarkModeContext';
import { lightTheme, darkTheme } from './themes';

function App() {
  const { isDarkMode } = useDarkMode();
  const theme = isDarkMode ? darkTheme : lightTheme;

  return (
    <ThemeProvider theme={theme}>
      {/* Your app content */}
    </ThemeProvider>
  );
}

2024 © All rights reserved - buraxta.com