Exploring the potential of integrating CSS Modules with Less in a React application to dynamically change

For my project, I have decided to use CSS Modules alongside Less which provides me with the best of both worlds.

Within my src folder, you will find the following structure:

components/
    [all components]
theme/
    themes/
        lightTheme.less
        darkTheme.less
    palette.less

palette.less:

@import './themes/lightTheme.less';

Each component that requires colors from the theme follows this pattern:

component.module.less:

@import '../../theme/palette.less';

.element {
    background-color: @primary;
}

This setup allows me to modify palette.less in order to import the desired theme. However, I would like to give users the option to choose their preferred theme dynamically. This means having both themes compiled and switchable at runtime.


An ideal solution could be structured like this:

app.less

body {
    @theme: @light-theme;

    &.dark-theme {
        @theme: @dark-theme;
    }
}

I envision importing the @theme variable in each component and accessing its properties (e.g. @theme[primary]).

Unfortunately, the scoping of Less variables does not support this approach.


I am willing to consider any solution that leverages Less modules.

Your thoughts are appreciated!

Answer №1

While many may be searching for a solution using Less/CSS modules, it's highly possible that your issue can be resolved simply by utilizing CSS variables, as pointed out by Morpheus.

How Does it Work?

To make this work, you need to ensure that all your styling avoids hardcoded values. Instead of:

.awesome-div {
  background-color: #fefefe;
}

You should use:

:root {
  --awesome-color: #fefefe;
}

.awesome-div {
  background-color: var(--awesome-color);
}

Switching Between Light and Dark Themes

There are two methods for changing themes in this manner:

  • Using vanilla JavaScript code within React to update the :root CSS element, check out this CodePen for more details;
  • Loading a component that contains all new :root variables in its component.css file;

In React (and vanilla CSS), multiple components or elements can declare their own :root in their .css files without any issues.

Moreover, any new :root declaration will take precedence over conflicting values from previous :root. This means that if we have a variable declared in app.css as :root { --color: red; } and then overwrite it in another component like component A with :root { --color: blue; }, the browser will render the value from component A.

Following this concept, you could have a dummy component that renders nothing, but inside its component.js file, import the .css file of a theme, for example:

import './light.css'; // Assume this is the light-theme dummy component

When swapping themes in your application, you can remove the dummy component from the scene and load the other one instead.

Although I'm not extremely familiar with CodePen when it comes to imports/modules, I hope the explanation above gives you an idea of what I'm trying to convey. Nevertheless, here's a simple pseudo-code illustrating my point:


loadTheme() {
  if (this.state.theme === 'dark') return <LightTheme />;
  if (this.state.theme === 'user-3232') return <UserTheme />;
  return <DarkTheme />;
}

render() {
  return (
    <App>
      {this.loadTheme()}
      <OtherContent>
    </App>
  );
}

Answer №2

If you're looking to implement CSS variables, one convenient option is to utilize the react-theme-change library.

Here's an example:

themes.ts

import ReactThemeChange from 'react-theme-change';

const base = {
    btn_radius: '50%',
};

const themes = {
    dark: {
        bg_0: 'rgba(21, 14, 65, 1)',
        title: 'rgba(255, 255, 255, 1)',
    },
    light: {
        bg_0: 'rgba(239, 242, 247, 1)',
        title: 'rgba(42, 49, 60, 1)',
    },
    gray: {
        bg_0: 'rgba(35, 42, 63, 1)',
        title: 'rgba(255, 255, 255, 1)',
    },
};
const useThemeChange = ReactThemeChange({
    base,
    themes,
    defaultTheme: 'light',
});

export default useThemeChange;    

Styles

button {
  width: 50px;
  height: 50px;
  border-radius: var(--btn_radius);
  cursor: pointer;
}

.demo {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  align-items: center;
  background-color: var(--bg_0);
  color: var(--title);
}

.themeName {
  font-size: 40px;
}

.btns {
  display: flex;
  gap: 8px;
}

Switch

import React from 'react';

import useThemeChange from './themes';
import './App.scss';

function App() {
    const {theme, setTheme} = useThemeChange();

    return (
        <div className="demo">
            <div className="themeName">Current Theme: {theme.name}</div>
            <div className="btns">
                <button onClick={() => setTheme('dark')}>Dark</button>
                <button onClick={() => setTheme('light')}>Light</button>
            </div>
        </div>
    );
}

export default App;

Answer №4

import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import navStyle from './navbar.module.css';
import './assets/css/global.css';


export default function NavigationBar() {
  const [mode, setMode] = useState('light');

  const toggleMode = () => {
    var root = document.querySelector(':root');
    var rootStyle = getComputedStyle(root);

    if(rootStyle.getPropertyValue('--Foreground--') === 'black'){
      root.style.setProperty('--Foreground--', 'white');
      root.style.setProperty('--Background--', 'black');
      setMode('dark');
    }else{
      root.style.setProperty('--Foreground--', 'black');
      root.style.setProperty('--Background--', 'white');
      setMode('light');
    }
  }

  return (
    <>
      <div className={navStyle.navbarDesign}>
        <div className={navStyle.logo}>
          <span className={navStyle.world}>World</span>
          <span className={navStyle.coder}>Coder</span>
          <span className={navStyle.master}>Master</span></div>
        <div className={navStyle.theme}>
          <i onClick={toggleMode} className={mode === 'light' ? `fa fa-sun ${navStyle.sunThemeIcon}` : `fa fa-moon ${navStyle.moonThemeIcon}`}></i>
        </div>
        <ul className={navStyle.listItems}>
          <li><Link className={navStyle.link} to="/">Home</Link></li>
          <li><Link className={navStyle.link} to="/service">Service</Link></li>
          <li><Link className={navStyle.link} to="/blog">Blog</Link></li>
          <li><Link className={navStyle.link} to="/about">About</Link></li>
          <li><Link className={navStyle.link} to="/">Login</Link></li>
        </ul>
        <div className={navStyle.searchGroup}>
          <input type="search" name="" id="" className={navStyle.navbarSearch} />
          <button className={navStyle.navbarSearchBtn}>Search</button>
        </div>
      </div>
    </>
  )
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way for me to customize the appearance of the Material UI switch component when it is in the checked

I am seeking to customize the color of the switch component based on its checked and unchecked state. By default, it displays in red. My goal is to have the "ball knob" appear yellow when the switch is checked and grey when it is not. This styling must be ...

Styling Overlapping Divs with CSS3

I am attempting to replicate this particular effect using HTML within the UIWebView control on iOS. The desired outcome is to simulate a progress bar on the listing. My current approach involved utilizing this method, however, as you can observe, adding pa ...

Position the center of an Angular Material icon in the center

Seeking help to perfectly center an Angular Material icon inside a rectangular shape. Take a look at the image provided for reference. https://i.sstatic.net/oFf7Q.png The current positioning appears centered, but upon closer inspection, it seems slightly ...

Failure of React Library in Production Mode on Webpack 4

Encountering an issue while trying to compile my react library in mode: production. After importing the library into another application, the following error message is displayed: Uncaught TypeError: Cannot set property props of #<TWithProps> which ...

Blogger website experiencing issue with Slick slider not automatically playing

I recently downloaded and customized the Slick Slider from the Slick Slider website and got it working perfectly on my local machine. However, when I added it to a blog website on blogger.com, I noticed that the auto play feature was not working and the n ...

Display or conceal several buttons using ReactJS

Currently, I am working on implementing a feature in ReactJS that involves showing and hiding different buttons for multiple view options, but I have hit a roadblock. Specifically, my task involves executing queries based on user input using reactJS. My g ...

Using NextJs to create a permanent redirect from the www version of a site to the non

I have developed a website using Nextjs (version 12.1.4). To enhance the SEO of my site, I want to create a permanent redirect from the www version to the non-www version. Typically, this can be achieved easily using nginx or an .htaccess file with apache. ...

Revamping the User Experience for Amazon Fire TV App

I've been working on creating a 3D application for the Amazon Fire TV using HTML5. I successfully developed and loaded it onto my Fire TV stick using the web app tester tool. Below is snippet of my code: #right{ width: 50%; display: inline-bl ...

Modifying the styles of every element on a webpage with JavaScript

I'm seeking the most effective method to dynamically update a stylesheet using JavaScript. Currently, I am aware that this can be achieved with querySelectorAll(), which retrieves all elements on a page that match a specific query. However, one drawba ...

Executing API calls directly within the `useEffect()` function

I am inquiring about the functionality of useEffect() in a function component. This script fetches data from a server and displays it. const NameList = (props) => { const [result, setResult] = useState([]); useEffect(() => { var url = `http ...

Is it possible to create a CSS checkbox without using an ID or "for" attribute

Is it possible to create a CSS checkbox with label without using id and for attributes? I have tried the following method, but it doesn't seem to work. I know that I can create a CSS checkbox with id and for attributes like this: <div class="chec ...

Tips for displaying a tooltip when hovering over a label in a Material UI slider

I'm currently working on a slider quiz and my goal is to have the tooltip appear when hovering over the label on the slider. Currently, I can only see the tooltip when I hover directly on the thumb at the location of my mouse. Refer to the image belo ...

What can I do to prevent a scrolling div that exceeds the height of the div itself?

I'm trying to align an image to the right side with a fixed position and have a large text content on the left side. I want the text content to be scrollable using bootstrap. Here is my code snippet. Thank you for your time and any suggestions! &l ...

Utilizing Bootstrap to allow for seamless text wrapping around a text input field

I am trying to implement a "fill-in-the-blank" feature using Bootstrap, where users need to enter a missing word to complete a sentence. Is there a way to align the text input horizontally and have the rest of the sentence wrap around it? This is my curr ...

Ways to assign values to array elements within the setState function

I am working with an array called nextDayWeather stored in the state and I need to update its values using the setState method. The goal is to pass this updated array to a component named WeekTemperatureControl, as shown in the code snippet below. Any gu ...

Design a webpage using material-ui components

I'm struggling to create a layout using material-ui for a child page. Can someone guide me on how to achieve this design? https://i.sstatic.net/TwYR5.png ...

Position the divs next to each other, ensuring that the right div has a minimum width

I need help aligning two div elements next to each other. However, if the right div has a minimum width and cannot fit in the available space, it should move below the left div! <div id="sidebar"> <div class="box"> <div class="l ...

Navigating by Typing in the URL Bar in React

Whenever I paste a valid URL and press enter, the useEffect function in the parent component does not get triggered. However, if I reload the page, it works fine. Here is the code snippet: Routing path <Route path="/search" element={<Searc ...

Box with plus symbol

One of my current projects involves creating a pill button with the following styling: <button class="button">Pill Button</button> .button { border: 1px solid red; color: black; padding: 10px 20px; text-align: center; text-d ...

Header navigation using jQuery

I'm encountering an issue with the final exercise in the jQuery course on Codecademy. My goal is to replicate the navigation header as accurately as possible, but I'm struggling to figure out how to keep the selected div highlighted after it&apo ...