In my project, I use CSS Modules with Less, which means I get the best of both worlds.
My src
folder looks something like this:
components/
[all components]
theme/
themes/
lightTheme.less
darkTheme.less
palette.less
palette.less:
@import './themes/lightTheme.less';
Then, in every component that wants to use colors from the theme, I do:
component.module.less:
@import '../../theme/palette.less';
.element {
background-color: @primary;
}
This structure lets me edit palette.less
to import the theme I want to use. The thing is that I want to let the users choose their preferred theme on their own. Themes should be switchable on runtime, which means I somehow need to have both themes compiled.
I imagine the perfect solution to be something like this:
app.less
body {
@theme: @light-theme;
&.dark-theme {
@theme: @dark-theme;
}
}
And then somehow import this @theme
variable in every component and read properties from it (i.e. @theme[primary]
).
Unfortunately, Less variables scoping don't work like this.
I am open-minded to any solution that uses Less modules.
Thank you!
CSS Modules is popular in React because of the following reasons: Scoped: CSS Modules are scoped when you use them the right way. Highly composable: You can compose different styles in a lot of ways. Tree shakable: Styles you don't use are removed, just like modern JS tooling.
You can import multiple css modules into a component or function using Object. assign For example if you import a button css modules to your Demo component, add this to the components default styles. Then in your component... start using pure css styles.
I know that you've probably looking for a solution that uses Less / CSS modules, but it's very likely that your situation can be solved solely with the use of css variables (as Morpheus commented on your question).
You'd have to ensure all your styling does not use hardcoded values, i.e. instead of:
.awesome-div {
background-color: #fefefe;
}
You would have:
:root {
--awesome-color: #fefefe;
}
.awesome-div {
background-color: var(--awesome-color);
}
There are two ways of changing themes in this approach:
:root
CSS element, check this codepen for more information;:root
variables in its component.css file;In React (in vanilla CSS too) you can easily have multiple components/elements declaring their own :root
at their .css files.
Furthermore, any new :root
will overwrite conflicting values from previous :root
. For example if at file app.css we have :root { --color: red; }
and, when loading another component, component A for instance, where in component_a.css we have the same variable overwritten, e.g. :root { --color: blue; }
the one rendered in our browsers will be the one from component A.
Following this logic, you can have a dummy component that does and renders exactly nothing, but instead in this component.js file you import the .css of a theme, e.g.:
import './light.css'; // suppose this is the light-theme dummy component
When switching themes in your app you'd just have to remove the dummy component from scene and call the other one.
I'm not too experienced with codepen to the point of providing you an example containing imports/modules over there, but I hope the above explanation can give you an idea of what I mean. Still, here's a brief pseudo-code of what I'm intending to demonstrate:
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>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With