I have an existing project with a scss file that uses semantic variables:
$background-color: white;
body {
background-color: $background-color;
}
I would like to change the background to black when I add a theming class to the body:
<body class="theme-dark">...</body>
and back to white if I remove the class (or switch to a theme-light).
I haven't found any light-weight methods to do this in scss (parametrizing a class for each theme seems like a very hard to maintain approach).
I've found a hybrid scss/css-custom properties solution:
original:
.theme-light {
--background-color: white;
}
update (based on Amar's answer):
:root {
--background-color: white;
}
.theme-dark {
--background-color: black;
}
$background-color: var(--background-color);
body {
background-color: $background-color;
}
Defining the scss variable as having a css-variable expansion as the value, i.e. (from above):
$background-color: var(--background-color);
generates the following css:
:root { --background-color: white; }
.theme-dark { --background-color: black; }
body { background-color: var(--background-color); }
which seems to be what we want...?
I like it since it only requires changing the definition of $background-color
(not every usage in a very large scss file), but I'm unsure if this is a reasonable solution? I'm pretty new to scss, so maybe I've missed some feature..?
We can dynamically update SCSS variables using ReactJS with the help of a project by achieving theme switching of the card component between light and dark theme.
Sass Set Color Functions An RGB color value is specified with: rgb(red, green, blue). Each parameter defines the intensity of that color and can be an integer between 0 and 255, or a percentage value (from 0% to 100%). Sets a color using the Red-Green-Blue-Alpha (RGBA) model.
The structure of SCSS rules First, choose one or more elements using IDs, classes, or other CSS selectors. Then, add styles. In this example, we select the elements with button class and add some properties. This is valid as CSS code as well as SCSS code.
SASS (Syntactically Awesome Style Sheets) is a pre-processor scripting language that will be compiled or interpreted into CSS. SassScript is itself a scripting language whereas SCSS is the main syntax for the SASS which builds on top of the existing CSS syntax.
Doing this with SCSS is possible but you would have to add styles to all elements you want to theme. That is because SCSS is compiled at build-time and you can't toggle the variables with classes. An example would be:
$background-color-white: white;
$background-color-black: black;
body {
background-color: $background-color-white;
}
.something-else {
background-color: $background-color-white;
}
// Dark theme
body.theme-dark {
background-color: $background-color-black;
.something-else {
background-color: $background-color-black;
}
}
The best way to currently do it is by using CSS variables. You would define the default variables like this:
:root {
--background-color: white;
--text-color: black;
}
.theme-dark {
--background-color: black;
--text-color: white;
}
Then, you would use these variables in your elements like this:
body {
background-color: var(--background-color);
color: var(--text-color);
}
If the body
element has the theme-dark
class, it will use the variables defined for that class. Otherwise, it will use the default root variables.
I would recommend an approach like what is mentioned in this Medium article. With this approach, you can assign what classes need to be themed without specifically mentioning the theme name so multiple themes can be applied at once.
First, you set up a SASS map containing your themes. The keys can be whatever makes sense to you, just make sure that each theme is using the same name for the same thing.
$themes: (
light: (
backgroundColor: #fff,
textColor: #408bbd,
buttonTextColor: #408bbd,
buttonTextTransform: none,
buttonTextHoverColor: #61b0e7,
buttonColor: #fff,
buttonBorder: 2px solid #fff,
),
dark: (
backgroundColor: #222,
textColor: #ddd,
buttonTextColor: #aaa,
buttonTextTransform: uppercase,
buttonTextHoverColor: #ddd,
buttonColor: #333,
buttonBorder: 1px solid #aaa,
),
);
Then use the mixin
and function
pair to add theme support.
body {
background-color: white;
@include themify {
background-color: theme( 'backgroundColor' );
}
}
.button {
background-color: lightgray;
color: black;
@include themify {
background-color: theme( 'buttonBackgrounColor' );
color: theme( 'buttonTextColor' );
}
&:focus,
&:hover {
background-color: gray;
@include themify {
background-color: theme( 'buttonBackgroundHoverColor' );
color: theme( 'buttonTextHoverColor' );
}
}
}
If you're going to be adding a lot of themes or a theme will be touching a lot of stuff, you might want to set up your SCSS files a little differently so that all the theming doesn't bloat your main CSS file (like the example above would do). One way to do this might be to create a themes.scss
file and replicate any selector paths that need theming and have a second build script that outputs just the themes.scss
file.
The Mixin
@mixin themify( $themes: $themes ) {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
@content;
$theme-map: null !global;
}
}
}
The Function
@function themed( $key ) {
@return map-get( $theme-map, $key );
}
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