1

i am trying to make a theme system on a react project that uses redux with a reducer that manages themes according to the user's local storage. But here my problem is that I used css files to define my styles on all of my components. However, I have all my logic with 2 javascript objects for light or dark mode. So I can't use js in css files unless I use css variables but I don't know how.

Here is my structure :

enter image description here

In my app.js I imported useSelector and useDispatch from react redux to access the global state :

import React from 'react';
import './App.css';
import Footer from './components/Footer';
import Header from './components/Header';
import Presentation from './components/Presentation';
import Projects from './components/Projects';
import Skills from './components/Skills';
import Timeline from './components/Timeline';
import { switchTheme } from './redux/themeActions';
import { useSelector, useDispatch } from 'react-redux';
import { lightTheme, darkTheme } from './redux/Themes';

function App() {

  const theme = useSelector(state => state.themeReducer.theme);
  const dispatch = useDispatch();
  
  return (
    <div className="App">
      <Header />
      <input type='checkbox' checked={theme.mode === 'light' ? true : false} 
      onChange={
        () => {
          if(theme.mode === 'light') {
            dispatch(switchTheme(darkTheme))
          } else {
            dispatch(switchTheme(lightTheme))
          }
      }} />
      <div className="top">
        <div className="leftPart">
          <Presentation />
          <Skills />
        </div>
        <Timeline />
      </div>
      <Projects />
      <Footer />
    </div>
  );

}

export default App;

and in themes.js I have my 2 objects which represent the themes :

export const darkTheme = {
    mode: 'dark',
    PRIMARY_BACKGROUND_COLOR: '#171933',
    SECONDARY_BACKGROUND_COLOR: '#1e2144',
    TERTIARY_BACKGROUND_COLOR: '#0a0c29',
    PRIMARY_TEXT_COLOR: '#eee',
    SECONDARY_TEXT_COLOR: '#ccc',
    PRIMARY_BORDER_COLOR: '#aaa'
}

export const lightTheme = {
    mode: 'light',
    PRIMARY_BACKGROUND_COLOR: '#D3CEC8',
    SECONDARY_BACKGROUND_COLOR: '#E5DFD9',
    TERTIARY_BACKGROUND_COLOR: '#C1BFBC',
    PRIMARY_TEXT_COLOR: '#222',
    SECONDARY_TEXT_COLOR: '#333',
    PRIMARY_BORDER_COLOR: '#555'
}

1 Answer 1

5

You can make use of data attributes. I have done the same in one my project like so :-

[data-color-mode="light"] {
  --color-focus-ring: #7daee2;
  --color-link-hover: #0039bd;
  --color-primary-bg: #eef6ff;
  --color-primary-text: #212121;
  --color-primary-border: #98b2c9;
  --color-secondary-bg: #c3d7f0;
  --color-secondary-text: #1a1a1a;
}

[data-color-mode="dark"] {
  --color-focus-ring: #5355d4;
  --color-link-hover: #4183c4;
  --color-primary-bg: #080808;
  --color-primary-text: #f1f1f1;
  --color-primary-border: #525252;
  --color-secondary-bg: #191919;
  --color-secondary-text: #d8d5d5;
} 

You can add the attribute to your top-level element (assuming div) like so:-

<div className="appContainer" data-color-mode="light" ref={appRef}> ></div>

Now use that appRef to change the data-color-mode attribute as well update the localstorage in one function. Updating the data-color-mode allows you to toggle between css variable colors easily. In my code, I have done this the following way:-

  const toggleColorMode = () => {
    const nextMode = mode === "light" ? "dark" : "light";
    // container is appRef.current only
    container?.setAttribute("data-color-mode", nextMode);
    setMode(nextMode);
  };

I am not using redux for this. Simply React Context API is being used by me but it's doable in your scenario as well.

You can take more reference from the repo - https://github.com/lapstjup/animeccha/tree/main/src

Note - I think there are other routes where people go with CSS-IN-JS but I haven't explored them yet. This solution is one of the pure css ways.

Fun fact - Github has implemented their newest dark mode in a similar way and that's where I got the idea as well. You can inspect their page to see the same attribute name :D.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.