Sass - What's the difference between map-get and simple variable? - css

I'm new with Sass stuff and I've been reading about different ways to use variables, this principle I'm trying to apply is just for colors, some of the solutions I've found were something like this (map-get):
$colors: (
lighestGray: #F8F8FA,
lightGray: #A5ACBA,
light: #FFFFFF,
dark: #000000,
link: #428bca,
linkHover: #555,
navBlue: #7AC243,
navGreen: #009CDC,
);
Then you use it on your class like this:
.my-class {
color: map-get($colors, dark);
}
And the other way is to use:
$color-black: #000000;
Then you use it like this:
.my-class {
color: $color-black;
}
My question is, which option is better? or map-getfunction has another purpose?, has Sass a pattern for this or it depends on each web-developer?.
Thanks for helping me out!.
Regards.

The differences is that when you use $map variables, they are best designed for using through iterations or using #each.
Sample case:
SCSS
// Map variable
$icons: (
facebook : "\f0c4",
twitter : "\f0c5",
googleplus : "\f0c6",
youtube : "\f0c7"
);
// Mixin doing the magic
#mixin icons-list($map) {
#each $icon-name, $icon in $map {
#if not map-has-key($map, $icon-name) {
#warn "'#{$icon-name}' is not a valid icon name";
}
#else {
&--#{$icon-name}::before {
content: $icon;
}
}
}
}
// How to use it
.social-link {
background-color: grey;
#include icons-list($icons);
}
CSS
// CSS Output
.social-link {
background-color: grey;
}
.social-link--facebook::before {
content: "";
}
.social-link--twitter::before {
content: "";
}
.social-link--googleplus::before {
content: "";
}
.social-link--youtube::before {
content: "";
}
This code was taken from my own answer in the following post but the answer is a case use of #each :)
Hope this help you

Example making a theme with css variables with fallback color
see codepen css variables
// VARS (FOR FALLBACK)
// -------------------
$theme-base: #70c1ac;
$theme-base-aa: adjust-color($theme-base, $blue: 125);
// PROCESSED THEME
$theme-color: $theme-base;
$theme-color-dark: darken($theme-color, 20%);
$theme-color-light: lighten($theme-color, 20%);
$theme-color-mixed: mix(#fff, $theme-color, 75%);
$theme-color-trans: transparentize($theme-color, .4);
// PROCESSED SECONDARY
$theme-color-aa: $theme-base-aa;
$theme-color-aa-dark: darken($theme-color-aa, 20%);
$theme-color-aa-light: lighten($theme-color-aa, 20%);
$theme-color-aa-mixed: mix(#fff, $theme-color-aa, 75%);
$theme-color-aa-trans: transparentize($theme-color-aa, .4);
$theme-colors: (
"aa-dark": $theme-color-aa-dark,
"aa-light": $theme-color-aa-light,
"aa-mixed": $theme-color-aa-mixed,
"aa-trans": $theme-color-aa-trans,
aa: $theme-color-aa,
dark: $theme-color-dark,
light: $theme-color-light,
mixed: $theme-color-mixed,
theme: $theme-color,
trans: $theme-color-trans,
);
#mixin themeColor ($prop, $color: null) {
#if ($color) {
#{$prop}: map-get($theme-colors, $color);
#{$prop}: var(--theme-color-#{$color})
} #else {
#{$prop}: map-get($theme-colors, theme);
#{$prop}: var(--theme-color);
}
}
#mixin setThemeColors($base1: "", $base2: "") {
// BASE THEME COLORS
$color-base: $theme-base;
$color-aa: $theme-base-aa;
#if ($base1) {
$color-base: $base1;
$color-aa: $base2;
}
// PROCESSED THEME COLORS
$color-aa-dark: darken($color-aa, 20%);
$color-aa-light: lighten($color-aa, 20%);
$color-aa-mixed: mix(#fff, $color-aa, 75%);
$color-aa-trans: transparentize($color-aa, .5);
$color-aa: $color-aa;
$color-dark: darken($color-base, 20%);
$color-light: lighten($color-base, 20%);
$color-mixed: mix(#fff, $color-base, 75%);
$color-trans: transparentize($color-base, .5);
// CSS VARIABLES
--theme-color-aa-dark: #{$color-aa-dark};
--theme-color-aa-light: #{$color-aa-light};
--theme-color-aa-trans: #{$color-aa-trans};
--theme-color-aa: #{$color-aa};
--theme-color-dark: #{$color-dark};
--theme-color-light: #{$color-light};
--theme-color-mixed: #{$color-mixed};
--theme-color-trans: #{$color-trans};
--theme-color: #{$color-base};
}
:root {
#include setThemeColors($theme-base, $theme-base-aa);
}
body {
#include themeColor("background","mixed");
font-size: 2rem;
}
ul {
list-style: none; /* Remove default bullets */
}
ul li::before {
content: "\2022"; /* Add content: \2022 is the CSS Code/unicode for a bullet */
#include themeColor("color","dark");
font-weight: bold; /* If you want it to be bold */
display: inline-block; /* Needed to add space between the bullet and the text */
width: 1.2em; /* Also needed for space (tweak if needed) */
margin-left: -.8em; /* Also needed for space (tweak if needed) */
}
li {
#include themeColor("color", "light");
#include themeColor("background", "aa-dark");
}

Why pick one when you can have them both.
_variables.scss
$color0 : white;
$color1 : red;
$color2 : green;
$color3 : blue;
_lists.scss
#use "variables";
#use "sass:map";
#use "sass:meta";
#use "sass:list";
#function dynamic($variable){
$i: 0;
$list: ();
#while(variable-exists($variable + $i)){
$list: list.append($list, map.get(meta.module-variables(variables), $variable + $i));
$i: $i + 1;
}
#return $list;
}
$colors: dynamic('color'); // white red green blue
Import both into your scss files and use the list when you need to loop and the variables for shorthand when applying styles.

map-get is used for getting css value from more kind of object.
suppose you have $param where you have defined multiple properties and now you want to assign. you can use it in following ways -
color: map-get($params, "color");
Where else simple variable holds only single value
map-get to get css value from object holding multiple values whereas
variable to hold single value

Related

map.get doesn't work in include mixin scss - Angular project

Github project
Hello every one ! I'm on an angular project and i'm stuck with my scss.
I have a mixin (in its own file mixins.scss :
BUTTON MIXINS
===================== */
#mixin btn($color:false,$bgColor:false,$size:false,$hoverBgColor:false){
button {
#if $color {
color: $color;
}
#if $bgColor {
background-color: $bgColor;
}
#if $size {
font-size: $size;
}
#if $hoverBgColor {
&:hover {
background-color: $hoverBgColor;
}
}
}
}
Also i have a global scss file with my variables :
$colors: (
primary-black: #000000,
primary-white: #FFFFFF,
medium-grey: #7A746E,
light-cream: #FFF7F0,
);
/* =====================
BUTTON VARIABLE
===================== */
$btnColor:(
btnHeaderFooterColor: map-get($colors, "primary-black"),
btnBody: map-get($colors, "light-red"),
);
When i want use my mixin for my button-component nothing happen, this is an example of my code.
button-component.scss :
#use "sass:map";
#import './../../../../styles/base/mixins';
#import './../../../../styles/base/global';
button {
.btn {
padding: 0.5rem 1.7rem;
border-radius: 1.8rem;
border-color: transparent;
&__headerFooter {
#include btn(map.get($btnColor, btnHeaderFooterColor));
}
}
}
I already tried #usebut i have the same result, my code doesn't appears in the browser inspector, i applied the class in my HTML, but if you want check the project the link is available just on top of this post.
I really appreciate your help because for me it's a mystery this situation !
Thanks you for reading me. :)

How to implement switchable themes in scss?

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..?
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.
All credit goes to Dmitry Borody
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 );
}

CSS :root variables and SASS Functions

in the Header of my HTML-Page i set the following CSS Variables:
:root{
--data-color-primary: #ffcc00;
--data-color-secondary: #4b4b4b;
}
In my SASS-File i use it as follow:
DIV.color {
&.color-primary {
background-color: var(--data-color-primary);
}
&.color-secondary {
background-color: var(--data-color-secondary);
}
}
Now i try to set the font-color depending on the brightness of the background-color:
#function set-notification-text-color($color) {
#if (lightness($color) > 60) {
#return #000000;
} #else {
#return #ffffff;
}
}
DIV.color{
&.color-primary {
background-color: var(--data-color-primary);
color: set-notification-text-color(var(--data-color-primary));
}
}
But in my SASS-compliler i get the following i get the following Error:
Error: argument $color of lightness($color) must be a color
How is ist possible to hand over der CSS variable to the function.
My Problem is, the CSS Variables are set in the Backend of my CMS by User (Liferay 7) an will be rendered in a *.FTL-File and is printed in the HTML-Code.
$primary: ${clr_primary};
$secondary: ${clr_primary};
and then i cant use the SASS-Variable $primary in my SASS-File.
Use SASS variables in your CSS variables.
$primary: #ffcc00;
$secondary: #4b4b4b;
:root{
--data-color-primary: #{$primary};
--data-color-secondary: #{$secondary};
}
Now call your mixin
DIV.color{
&.color-primary {
background-color: $primary;
color: set-notification-text-color($primary);
}
}
Another options would be to create a mixin which retrieves the CSS variable
#function color($color-name) {
#return var(--data-color-#{$color-name});
}
Now call that function like so
DIV.color {
&.color-primary {
background-color: color(primary);
color: set-notification-text-color(color(primary));
}
}
Check out this link for usefull information about combining CSS and SASS variables
https://codepen.io/jakealbaugh/post/css4-variables-and-sass
If you need to change CSS variables outside of :root you can do the following
.class {
// sass-lint:disable no-duplicate-properties
#{--background}: transparent;
#{--background-focused}: transparent;
// sass-lint:enable no-duplicate-properties
}
and this compiles to
.class {
--background: transparent;
--background-focused: transparent;
}

SASS – looping through map via mixin doesn’t compile CSS

what I have is a simple SASS color map:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
The following:
#each $brand, $col in $brand_col {
body.#{$brand} {
background: $col;
}
}
leads to expected output:
body.def { background: blue; }
body.mus { background: red; }
body.ser { background: yellow; }
When I try to put the same thing into a mixin like so:
$color: null;
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
$color: $col;
#content;
}
}
}
.body { #include branding { background: $color; } }
I would expect the same output, but nothing is getting compiled at all. I copied the mixin from a sass specific site and don’t fully understand the whole process. Any hints what I'm doing wrong?
Thanks
Ralf
To achive the same result as in your first example, have two options:
Option 1
Make a simple non-reusable mixin:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
background: $col;
}
}
}
.body {
#include branding;
}
This will compile to:
.body.def {
background: blue;
}
.body.mus {
background: red;
}
.body.ser {
background: yellow;
}
Option 2
Make a reusable mixin, so you can pass the color map to apply:
$brand_colors: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding($colors) {
#each $class, $color in $colors {
&.#{$class} {
background: $color;
}
}
}
.body {
#include branding($brand_colors);
}
// Latter you can use it to apply the same 'branding' for any other element
div {
#include branding($brand_colors);
}
Will compile to:
.body.def {
background: blue;
}
.body.mus {
background: red;
}
.body.ser {
background: yellow;
}
div.def {
background: blue;
}
div.mus {
background: red;
}
div.ser {
background: yellow;
}
You could even implement a second parameter to the mixin to specify which css property you want to apply, with background as a default:
#mixin branding($colors, $property: background) {
#each $class, $color in $colors {
&.#{$class} {
#{$property}: $color;
}
}
}
// Latter you can use it to apply the same 'branding' for any other element and property
h1 {
#include branding($brand_colors, color);
}
Will compile to:
h1.def {
color: blue;
}
h1.mus {
color: red;
}
h1.ser {
color: yellow;
}
You can find out more about mixins here.
Hope it helps!
What do you mean by $color: $col;? no such property like "null" in CSS, because when you set $color: null at top and then trying to set a property $color: $col; you actually trying to set like that null: blue; this is nothing mean anything to compiler.
I think you no need #content directive use here. You should try just following way:
$brand_col: (
def: blue,
mus: red,
ser: yellow
);
#mixin branding {
#each $brand, $col in $brand_col {
&.#{$brand} {
background: $col;
}
}
}
.body { #include branding(); }

Is it possible to take the variable's name in SASS?

I tried to make the following code look fancier with an #each or a #for loop (without any usable result).
.btn[data-btn-color="black"] {
#include colored-btn($black);
}
.btn[data-btn-color="blue"] {
#include colored-btn($blue);
}
.btn[data-btn-color="red"] {
#include colored-btn($red);
}
// ... and more colors ...
My current approach is to take value from the variable to use it as the value for the data-btn-color attribute and put that snippet into an #each loop.
Something like
#each $color in ($black, $blue) {
#include colored-btn($color);
}
which compiles into:
.btn[data-btn-color="black"] {
background-color: #000; // $black
}
.btn[data-btn-color="blue"] {
background-color: #00f; // $blue
}
Is there any function, which allows me to do such a thing?
You were so close! You don't want the () around what you want to go through in #each. I think Sass would just see what you have as one list item with a two item list inside.
Here is what I think you're trying to do:
$red: #f00;
$blue: #00f;
$black: #000;
$colors: red $red, blue $blue, black $black;
#mixin colored-button($background-color: #000){
background-color: $background-color;
}
#each $color in $colors {
$name: nth($color, 1);
$hex: nth($color, 2);
.btn[data-btn-color="#{$name}"]{
#include colored-button($hex);
}
}
Which would result in:
.btn[data-btn-color="red"] {
background-color: red; }
.btn[data-btn-color="blue"] {
background-color: blue; }
.btn[data-btn-color="black"] {
background-color: black; }

Resources