DEV Community

Mohamed Idris
Mohamed Idris

Posted on

How to Easily Implement a Dark Theme Perfectly

Implementing a dark theme is simple and can be done effectively using CSS and JavaScript. Here's a step-by-step guide to make it work perfectly on your website.

1. CSS Variables for Dark Theme

Start by defining CSS variables for the light and dark theme colors. This allows for easy switching between themes.

/* Light theme variables */
--grey-50: #f8fafc;
--grey-900: #0f172a;
--dark-mode-bg-color: #333;
--dark-mode-text-color: #f0f0f0;
--backgroundColor: var(--grey-50);
--textColor: var(--grey-900);
--darkModeTransition: color 0.3s ease-in-out, background-color 0.3s ease-in-out;

body {
  background: var(--backgroundColor);
  color: var(--textColor);
  transition: var(--darkModeTransition);
}

body.dark-theme {
  --textColor: var(--dark-mode-text-color);
  --backgroundColor: var(--dark-mode-bg-color);
}
Enter fullscreen mode Exit fullscreen mode

Note: Instead of manually updating color and background-color properties, we update the CSS variables directly in body.dark-theme. This method is much easier because it avoids needing to check through the whole app’s code. We're only changing the variables, making the process more scalable and maintainable.

You can also automatically switch the theme based on the user's system preferences by using a media query (though we’ll do it manually with JavaScript, so this is commented out here):

/* @media (prefers-color-scheme: dark) {
  :root {
    --textColor: var(--dark-mode-text-color);
    --backgroundColor: var(--dark-mode-bg-color);
  }
} */
Enter fullscreen mode Exit fullscreen mode

2. JavaScript Logic for Theme Switching

We'll use React and useContext to handle the dark theme logic. Here's the setup:

import { createContext, useContext, useEffect, useState } from 'react';

// Get initial dark mode state based on system preference or localStorage
const getInitialDarkMode = () => {
  const prefersDarkMode = window.matchMedia('(prefers-color-scheme:dark)').matches;
  const storedDarkMode = localStorage.getItem('darkTheme') === 'true';

  if (storedDarkMode === null) {
    return prefersDarkMode;
  }

  return storedDarkMode === 'true';
};

const AppContext = createContext();

export const AppProvider = ({ children }) => {
  const [isDarkTheme, setIsDarkTheme] = useState(getInitialDarkMode());

  const toggleDarkTheme = () => {
    setIsDarkTheme((prev) => {
      const newValue = !prev;
      localStorage.setItem('darkTheme', newValue);
      return newValue;
    });
  };

  useEffect(() => {
    document.body.classList.toggle('dark-theme', isDarkTheme);
  }, [isDarkTheme]);

  return (
    <AppContext.Provider value={{ isDarkTheme, toggleDarkTheme }}>
      {children}
    </AppContext.Provider>
  );
};

export const useGlobalContext = () => useContext(AppContext);
Enter fullscreen mode Exit fullscreen mode

3. Dark Mode Toggle Button

Now, let's add a button to toggle between light and dark mode:

import { BsFillSunFill, BsFillMoonFill } from 'react-icons/bs';
import { useGlobalContext } from '../context';

const ThemeToggle = () => {
  const { isDarkTheme, toggleDarkTheme } = useGlobalContext();

  return (
    <section className='toggle-container'>
      <button className='dark-toggle' onClick={toggleDarkTheme}>
        {isDarkTheme ? (
          <BsFillMoonFill className='toggle-icon' />
        ) : (
          <BsFillSunFill className='toggle-icon' />
        )}
      </button>
    </section>
  );
};

export default ThemeToggle;
Enter fullscreen mode Exit fullscreen mode

Conclusion

With just a few lines of CSS and JavaScript, you can easily implement a dark theme that adapts to the user's system preferences and provides a seamless transition when toggled. The key is using CSS variables for dynamic styling and React to manage the theme state.

That's it! Now you have a dark mode toggle feature ready for your application. ✨

Top comments (1)