How to create multiple themes in sass unsing sass modules feature? - css

Before modules were introduced to sass, you could make a themed stylesheet this way:
themeDark.scss:
$color: black;
baseButton.scss:
button {
color: $color;
}
button.scss:
#import './_themeDark.scss'
#import './_baseButton.scss'
Now, modules were introduced to sass and #import is getting deprecated. How to achieve the same effect using sass modules?
Im looking for a solution, where some files are used as partial stylesheets. In the main file they are combined to form a final stylesheet that will be imported.
What i have tried:
use #use instead of #import. It does not work, because variables from other modules are not seen.
use mixins. The problem is that you have to pass all the variables through arguments and it is much harder to maintain than the solution above with #import.

You can have scss map to store all themes
$themes: (
theme1: (color: red),
theme2: (color: orange),
theme3: (color: yellow),
theme4: (color: green),
theme5: (color: blue)
);
Using this map is with an #each loop.
#each $theme, $map in $themes {
.#{$theme} {
color: map-get($map, color);
}
}
On each loop, assign these values to $theme and $map respectively.
$theme - Theme name
$map - Map of all theme variables
You now use the map-get() function to get any theme variable from $map and output the correct property for each theme.
#each $theme, $map in $themes {
.#{$theme} & { // <--- Notice the & here!
color: map-get($map, color);
}
}

Related

use SCSS to generate both defaults and css variables

I'm implementing a websites style.
For legacy support reasons, I need to support IE11, at least for a while. For workflow and my sanity reasons, I would like to use css variables where possible.
I have looked at this solution, which would generate something that works, but it is quite verbose to use.
My goal is to end up with a hardcoded value, which is overwritten by a CSS variable immediately. This way I can add a default theme for everyone, and different themes (like darkmode) for those browsers that support css variables. Obviously without needing to write all of that myself.
So the idea would be that I can write something like this:
$foo {...?}
:root {
--foo: red;
}
:root.dark {
--foo: black;
}
p {
color: $foo;
}
and it gets transpiled to
:root {
--foo: red;
}
:root.dark {
--foo: black;
}
p {
color: red;
color: var(--foo);
}
Is this possible with scss? I do not want to add some random npm modules or other third party compilers and transpilers to this project to bloat it.
I know of the possibility of adding a polyfill for IE11 that adds support for CSS variables, but most that I've found so far have some form of unfortunate limitation (plus again, they are third party code that I would prefer to avoid if I can). If there is no nice solution using SCSS, that is probably what I will go with.
Here is a quick solution you might want to improve:
Define a map with all your colors:
$colors: ("blue": #0000ff, "red": #ff0000, "green": #00ff00);
Loop over the map to create the css custom properties:
#each $color, $value in $colors {
:root {
--#{$color}: #{$value};
}
}
Create a mixin to output both the fallback and the value.
I've decided to create a mixin that takes 2 params, the css property and the color you want.
#mixin propertyPlusColorValue($property, $color) {
#{$property}: map.get($colors, $color);
#{$property}: var(--#{$color});
}
Then you can use it like this:
.foobar {
#include propertyPlusColorValue(color, "blue");
#include propertyPlusColorValue(background-color, "red")
}
Full code:
#use "sass:map";
$colors: ("blue": #0000ff, "red": #ff0000, "green": #00ff00);
#each $color, $value in $colors {
:root {
--#{$color}: #{$value};
}
}
#mixin propertyPlusColorValue($property, $color) {
#{$property}: map.get($colors, $color);
#{$property}: var(--#{$color});
}
.foobar {
#include propertyPlusColorValue(color, "blue");
#include propertyPlusColorValue(background-color, "red")
}

is there a way to use bootstrap 5 custom color directly in the class attribute?

like we do:
<span class="text-primary">some text</span>
I want to know if there is a way to do:
<span class="text-red-300">some text</span>
red-300 is a bootstrap 5 custom color and there are others in the following link.
https://getbootstrap.com/docs/5.0/customize/color/
There is no way to use $red-300 directly in CSS. It's only possible using SASS because $red-300 is a SASS variable.
However, the base colors are also available as CSS variables. For example, --bs-red is the same as $red and $red-500. So, it could be used as a CSS variable like this...
.text-red-500 {
color: var(--bs-red);
}
CSS Demo
If you wanted to use SASS for $red-300, it would be done like this:
#import "functions";
#import "variables";
.text-red-300 {
color: $red-300;
}
SASS demo
Indeed there are no Bootstrap native classes or mixins to realise that out of the box. On the other hand ... Bootsstrap is build to be extended: Using SASS you can extend Bootstrap and create the helper classes on the fly.
NOTE: The SASS code example below is done to YOUR QUESTION (= all colors = 900 additional helper classes). Maybe you may like to reduce that huge amount of additional code by building the color classes only to your indeed needed colors. Please feel free to adapt that code to your needs.
//### make sure you have imported bootstrap function & variables first
#import 'functions';
#import 'variables';
//### SASS function to extend Bootstrap
//### --> quick and dirty demo example!!!
#mixin make-color-classes( $class_prefix, $color_name, $color ){
// note: positive values = tint | negative values = shade
$swatch_variations: (80%, 60%, 40%, 20%, 0, -20%, -40%, -60%, -80%);
$i: 1;
#each $variation in $swatch_variations {
// process bootstrap color
#if ($variation > 0){
$swatch_color: tint($color);
}#else if ($variation < 0) {
$variation: $variation * -1;
$swatch_color: shade($color);
}#else{
$swatch_color: $color;
}
// write class
$color_number: $i * 100;
.#{$class_prefix}-#{$color_name}-#{$color_number} {
color: tint-color($color, $variation );
}
$i: $i + 1;
}
}
//### NOW:
//### create helper classes for a single color
#include make-color-classes('text', 'blue', $blue);
//### NOW EXTENDED:
//### create helper classes for all named default bootstrap colors
$colors_to_use: $colors;
#each $color_name, $color in $colors_to_use {
#include make-color-classes('text', $color_name, $color);
}

Generating css variables with scss #each not working

I am working with ionic 4 project and scss. I am having some strange behaviour, while I am trying to create a global CSS variables using #each:
:host {
$colors-availability: (
early-day: #e8ab00,
long-day: #854fa5,
whole-day: #fe307b,
all-day: #32773e,
morning: #87cc93,
afternoon: #4bb15d,
late: #fa8072,
twilight: #40e0d0,
night: #16151b
);
#each $name, $color in $colors-availability {
--ion-color-#{$name}: $color;
}
}
This is my code in theme/variables.scss file. I am expecting to have all these variable with respecting colours, but the output is this:
I don't have the hash colour values. Any idea? I tried everything(color() function, etc..). Thanks.
Try something like this as below:
--ion-color-#{$name}: #{$color};

CSS variables with fallback for older browsers

TL;DR: How can you use SCSS to have CSS variables with a fallback for older browsers.
I'm trying to make sense of this article. In my opinion, you have to already be an advanced SASS user to understand it, which I'm not. To make matters worse, it's the only article I found on the subject.
Here is what I'm trying to achieve:
My scss should be along the lines of :
body {
#include v(background-color, primary)
}
then the processed CSS should be
body{
background: yellow; /* Yellow being defined above as the primary color */
background: var(--color-primary);
}
By playing around a bit, I can already get the value of the CSS variable like so:
$colors: (
primary: yellow,
);
:root {
#each $name, $color in $colors {
--color-#{$name}: $color;
}
}
#mixin background-color($color_) {
background: var(--color-#{$color_});
}
To use it:
body{
#include background-color(primary);
}
Which will result in this:
body {
background: var(--color-primary);
/* But the fallback is missing :(, I tried things with the map-get but it's really eluding me... */
}
If you're using Sass, you can automate fallbacks through a Sass mixin. Create a map of your CSS variable names and their values, and then you can look up those values in a mixin that outputs the fallback style and the preferred one
$vars: (
primary: yellow,
);
:root {
--primary: map-get($vars, primary);
}
#mixin var($property, $varName) {
#{$property}: map-get($vars, $varName);
#{$property}: var(--#{$varName});
}
The above mixin is used like so:
body {
#include var(background-color, primary);
}
and outputs the following CSS:
:root {
--primary: yellow;
}
body {
background-color: yellow;
background-color: var(--primary);
}
Et voilĂ  :)
Update: Postcss Custom properties can do fallback and is way easier than the below code
step 1: declare scss variables
So first of all we want to put some variables in a $map, I'll go with color variables:
$colors: (
primary: #FFBB00,
secondary: #0969A2
);
step 2: automate css 4 var generation
// ripped CSS4 vars out of color map
:root {
// each item in color map
#each $key, $value in $colors {
--colors-#{$key}: $value;
}
}
What happens in root is : for each key and value in the colors map, we print the followng :
--colors-#{$key}: $value;
Which corresponds to css variable declarations. I believe the weird bit with #{} around the key is to not have spaces around the value.
Thus the result is:
--colors-primary: #FFBB00,
--colors-secondary: #0969A2
Note that the prefix (--colors-) is the same name as the scss color map above it. The why will become clear in last step.
step 3: Plenty of maps !
$props: (
background-color: $colors
);
$map-maps: (
background-color: colors
);
Here we add the map $props which maps a css property to the map containing the values. background-color will hold color, so the correct map is $colors.
map-maps is a copy of props where instead of the map we have the name of said map. (this is relative to the note in step 2).
Step 4 : let's make it work !
#mixin v($prop, $var) {
// get the map from map name
$map: map-get($props, $prop);
// fallback value, grab the variable's value from the map
$var-fall: map-get($map, $var);
// our css4 variable output
$var-output: var(--#{$map}-#{$var});
#{$prop}: $var-fall;
// css4 variable output
#{$prop}: $var-output;
}
body{
#include v(background-color, primary);
}
I simplified the code in the article quite a bit, it still works, for this example at least, the code in the article takes more into account.
Anyhow, here is what happens.
First, we call the mixin with:
#include v(background-color, primary);
Then upon entering,
$map: map-get($props, $prop); // map-get($props, background-color)
we have a variable called $map to which we assign the value that is inside the $props map at the key background-color which happen to be the $colors map. It's a bit of a maze but it's not that complicated once you resolve it.
Then for the fallback:
$var-fall: map-get($map, $var);
This simply gets the value of the map we just got (which is $colors) at the $var key (which happens to be primary). Thus the result is #FFBB00.
For the css var
$map-name: map-get($map-maps, $prop);
$var-output: var(--#{$map-name}-#{$var});
we recreate what we did to generate the var in the #each loop
Whole code would be :
$colors: (
primary: #FFBB00,
secondary: #0969A2
);
// ripped CSS4 vars out of color map
:root {
// each item in color map
#each $name, $color in $colors {
--colors-#{$name}: $color;
}
}
$props: (
background-color: $colors,
color: $colors
);
$map-maps: (
background-color: colors
);
#mixin v($prop, $var) {
// get the map from map name
$map: map-get($props, $prop);
// fallback value, grab the variable's value from the map
$var-fall: map-get($map, $var);
// our css4 variable output
$map-name: map-get($map-maps, $prop);
$var-output: var(--#{$map-name}-#{$var});
#{$prop}: $var-fall;
// css4 variable output
#{$prop}: $var-output;
}
body{
#include v(background-color, primary);
}
Now this is a simplification of what is done in the article. You should check it out to have code a bit more robust.
I assume you are aware of the reason why it didn't show the fallback. But since it's an answer I will explain the reasons
The current mixin block has only one background property which makes the sass compiler to generate only one property. I don't think sass can identify whether 'var' is supported in browser or not. So, we have to explicitly specify if we need the fallback.
Since you already have the map all you need is to get the value by giving the key 'primary'
#mixin background-color($color_) {
background: var(--color-#{$color_});
background: map-get($colors, primary);
}
This will add the background: yellow to the body class always. Alternatively if you want to control the addition of the fallback based on condition. You can do like this
#mixin background-color($color_, $showFall) {
background: var(--color-#{$color_});
#if $showFall {
background: map-get($colors, primary);
}
}
and call like this
body{
#include background-color(primary, true);
}
Code pen for the same
https://codepen.io/srajagop/pen/xdovON
Note: I am writing the answer under the assumption that you want only the background-color to work and not all the other properties like mentioned in that post. For that you need to create a proper data structure

CSS - SASS: Using #each based mixins to generate multiple backgrounds

I am using CSS to create a catalog of hairstyles and colours. I have a combination of 55 different colours and hair styles. Each hair style and colour has its own image SVG file and I need to combine them all into a single background (Using the multiple background feature of CSS3).
I have written this mixing to generate the multiple backgrounds: (It is based on the mixin in this post)
#mixin style-matrix($colors, $styles) {
#each $s in $styles {
#each $c in $colors {
url("pps#{$s}#{$c}.svg"),
}
}
}
$colors: blonde, red, dkbrown, ltbrown, black;
$styles: hairboy1, hairboy2, hairboy3, hairboy4, hairboy5, hairgirl6, hairgirl1, hairgirl4, hairgirl2, hairgirl3, hairgirl5;
.hidden {
background: #include style-matrix($colors, $styles) url("base.svg);
}
(see codepen here)
However, every time I run the mixin, I get this error message:
Invalid CSS after "... url": expected "{", was "("pps#{$s}#{$c}..."
How can I use the mixin to generate the multiple backgrounds?
Mixins return property/value pairs. If you want the value only, you need a function. It looks like this:
#function style-matrix($colors, $styles) {
$bg: compact();
#each $s in $styles {
#each $c in $colors {
$bg: join($bg, url("pps#{$s}#{$c}.svg"), comma);
}
}
#return $bg;
}
.hidden {
background: style-matrix($colors, $styles), url("base.svg");
}

Resources