Dynamically create variable with sass in list - sass

Basically, I'm trying to generate a lot of styles, each containing an image and a color. Colors are listed and variables are named the same way. The problem is I can't have Sass to use the dynamically generated name (near a{color }. But is it possible to use it this way ? Thanks !
$color-style-winter: #11111;
$color-style-christmas: #22222;
$styles: 'winter' 'hills',
'christmas' 'xmas';
#each $name, $image in $styles {
.style-#{$name} {
background: url('../../images/styles/#{$image}.jpg');
}
a {
color: $color-style- + $name;
}
}

I'm not sure I understood your question fully but have a look on SASS interpolation Docs and the article provided. Use placeholder.
The code could look like:
%my-style-test1 {color: red;}
%my-style-test2 {color: blue;}
$style: 'test1' 'test2';
#each $name in $style {
  a {
    #extend %my-style-#{$name};
  }
}
CSS:
a {
color: red;
}
a {
color: blue;
}
the example is a bit useless but shows how to use % placholder and interpolation
SASS Docs interpolation
SASS Articel Interpolation

Related

SASS/SCSS, how to access a property/method in a dynamic way from a partial file?

Let's say for instance we have the next sass partial file:
//_colors.scss
$foo: red;
And we "use" it on another file:
//test.scss
#use './colors'
.test{
color: colors.$foo;
}
All good, but what if I would like to use/get the value in a dynamic way within a mixin? something like:
//test.scss
#use './colors'
#mixin getColor($type){
color: colors[$type]; //JavaScript example, * don't actually work *.
or
color: #{colors.{$type}; * don't work neither *
//The above returns `color: colors.foo` instead of `color: red` on compilation.
or
color: colors.#{$type}; * doesn't work neither *
}
.test{
#include getColor(foo);
}
Is it possible? thanks for the help!
For a color, I really much prefer a function so it can be used on any property (color, background-color, border, box-shadow...)
I usually declare a string equivalent to variable names, then define them inside a map. Finally this map is accessible via a dedicated function.
Something like
//_colors.scss
#use 'sass:map';
$favoriteRed: "favoriteRed";
$favoriteYellow: "favoriteYellow";
$favoriteBlue: "favoriteBlue";
$MyColors: (
$favoriteRed: #c00,
favoriteYellow: #fc0,
$favoriteBlue: #0cf
);
#function my-color($tone: $favoriteRed) {
#if not map.has-key($MyColors, $tone) {
#error "unknown `#{$tone}` in MyColors.";
}
#else {
#return map.get($MyColors, $tone);
}
}
This _colors.scss generates no code at all, it can be imported anywhere at no cost.
Then, in a specific style file:
//test.scss
#use './colors' as *;
//inside a mixin
#mixin special-hue-component($tone){
div.foo {
span.bar {
border-color: my-color($tone);
}
}
}
//or directly
.foobartest {
color: my-color($favoriteBlue);
}

Looping and assigning values through SASS

Before judging my situation, I am not using a typical Bootstrap approach to assign custom colors to variables. I am in a unique situation of depending on the Bootstrap CDN, and re-creating custom SASS variables that look like BS4 variables. Read on!
I feel like I am so close on the the following process. All I want to do is assign my array values to a class property name like so, (i.e. background-color: $theme-primary!important;)
//ORIGINAL THEME VARIABLES
$theme-colors: (primary:$m-color-blue, secondary: $m-color-off-white, success: $m-color-grey, info: $m-color-grey-light, warning: $m-color-gold, light: $m-color-white, dark: $m-color-grey-dark);
$theme-primary: map-get($theme-colors, "primary");
$theme-secondary: map-get($theme-colors, "secondary");
$theme-success: map-get($theme-colors, "success");
$theme-info: map-get($theme-colors, "info");
$theme-warning: map-get($theme-colors, "warning");
$theme-light: map-get($theme-colors, "light");
$theme-dark: map-get($theme-colors, "dark");
//MY LOOP TO ASSIGN BS4 BG COLORS TO MY CUSTOM COLORS.
$classes: primary secondary success warning danger light;
#each $class in $classes {
html body .bg-#{$class} {
//MY ISSUE IS HERE...IT DOES NOT LIKE HOW I AM FORMING THIS PROPERTY. SYNTAX ISSUE???
background-color: $theme-#{class} !important;
}
}
But when I attempt to compile it, I get the following error:
messageOriginal: Undefined variable: "$theme-".
I think I get the error, but how do I resolve?
I'm not sure why this would be necessary since there's already utility classes available for this; https://getbootstrap.com/docs/4.0/utilities/colors/#background-color
You can also feed the bootstrap sass straight into your build pipeline to use all their vars, mixins, functions already;
https://getbootstrap.com/docs/4.0/getting-started/theming/
However, I think you're looking for something more like this amigo; Cheers
$classes: (
primary: "#f00",
secondary: "#ddd",
success: "#00f",
warning: "#0f0",
danger: "#f00",
light: "#eee"
);
#each $key, $val in $classes {
.bg-#{$key} {
background-color: #{$val} !important;
}
}
If you're not importing the $theme variable from your _base / other directory then how do you expect the script to know what to fill it in with?
Your syntax is wrong, you need to wrap $theme with #{} as well so it's #{$theme}-#{class}
working example:
$classes: primary secondary success warning danger light;
$theme: 'blue'; // switch this with import theme.
#each $class in $classes {
html body .bg-#{$class} {
background-color: #{$theme}-#{$class} !important;
}
}
generated css:
html body .bg-primary {
background-color: blue-primary !important;
}
html body .bg-secondary {
background-color: blue-secondary !important;
}
html body .bg-success {
background-color: blue-success !important;
}
html body .bg-warning {
background-color: blue-warning !important;
}
html body .bg-danger {
background-color: blue-danger !important;
}
html body .bg-light {
background-color: blue-light !important;
}
If you are using Bootstrap4, you can directly add a new color to $theme-colors, add the new key and value
$theme-colors: (
"custom-color": #900
);

Concancate loop variable with string to produce another variable on the fly

I am trying make this mixing work.. Any ideas how to concancate a variable name on the fly and make it processed.
$colors: purple pink;
#each $color in $colors {
.box--#{$color} {
background-color: #{'$ui'}-$color;
}
}
In this case $ui-red is a red color variable.
Unfortunately, you can't generate or reference to sass single variables in runtime. But you can store your color codes and names in sass maps (requires sass v3.3) and use it in cycle like this:
$colors: ("purple": #f7f,
"pink": #ffa);
#each $color-name, $color-code in $colors {
.box--#{$color-name} {
background-color: $color-code;
}
}
In CSS you get:
.box--purple {
background-color: #f7f;
}
.box--pink {
background-color: #ffa;
}
Example: http://www.sassmeister.com/gist/c1285109946e5207e441c7ee589dd382

Sass configuration map with default values

I am creating css using SASS and would like to make it possible for another developer to create a custom css by changing sass variables. This works fine when I in my base file use a single variable like this:
$text-color: #000 !default;
To test the override I create a new project where I first declare an override for the variable and then import the "base" sass file.
$text-color: #0074b;
#import "base-file";
But I would also like to use maps for configuration but then I do not get the override to work. How should I use configuration maps that can be overriden?
$colors: (text-color: #000, icon-color: #ccc );
Adding !default after #000 gives me a compilation error: expected ")", was "!default,")
Adding !default after the ) gives no error but the variables does not get overwritten either.
Any ideas on what I am doing wrong?
I don't think the functionality you want exists in standard Sass. I built this function though that does what you're asking for:
//A function for filling in a map variable with default values
#function defaultTo($mapVariable: (), $defaultMap){
//if it's a map, treat each setting in the map seperately
#if (type-of($defaultMap) == 'map' ){
$finalParams: $mapVariable;
// We iterate over each property of the defaultMap
#each $key, $value in $defaultMap {
// If the variable map does not have the associative key
#if (not map-has-key($mapVariable, $key)) {
// add it to finalParams
$finalParams: map-merge($finalParams, ($key : $value));
}
}
#return $finalParams;
//Throw an error message if not a map
} #else {
#error 'The defaultTo function only works for Sass maps';
}
}
Usage:
$map: defaultTo($map, (
key1 : value1,
key2 : value2
));
Then if you have a mixin for something, you can do this sort of thing:
#mixin someMixin($settings: ()){
$settings: defaultTo($settings, (
background: white,
text: black
);
background: map-get($settings, background);
color: map-get($settings, text);
}
.element {
#include someMixin((text: blue));
}
Outputted CSS:
.element { background: white; color: blue; }
So you would use it like this based on what you said in the question:
$colors: defaultTo($colors, (
text-color: #000,
icon-color: #ccc,
));
Bootstrap has solved this issue as:
$grays: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$grays: map-merge(
(
"100": $gray-100,
"200": $gray-200,
"300": $gray-300,
"400": $gray-400,
"500": $gray-500,
"600": $gray-600,
"700": $gray-700,
"800": $gray-800,
"900": $gray-900
),
$grays
);
https://github.com/twbs/bootstrap/blob/v4.1.3/scss/_variables.scss#L23

Using interpolation for calling compass sprite mixins

the problem that i have with this mixing is that i can't interpolation for calling different sprite mixins that compass provides.
I want to be able to have this in one place, create the sprites in different scss and them just include this shared mixing and use it.
So far seems that Sass doesn't allow me to do that. Maybe i just have a crazy and bad way of doing things ( i'm no designer really and i learn about sass a few months ago ).
Thanks for your time.
#mixin icon-button($width, $height, $icon-bg-color, $icon, $sprite-name){
.icon-base{
width: $width;
height: $height;
background-color: $icon-bg-color;
.icon{
$icon-height: #{$sprite-name}-sprite-height(#{$icon});
$icon-width: #{$sprite-name}-sprite-width(#{$icon});
#include #{$sprite-name}-sprite(#{$icon});
width: $icon-width;
height: $icon-height;
position: relative;
left: ($width - $icon-width)/2;
top: ($height - $icon-height)/2;
}
}
}
The #{something} is a ruby-way of interpolation. Compass framework uses ruby compiler to make css from scss/sass. Interpolation is just the way to insert some value (of a variable) into a string, like this: puts "I want to say #{smth}" will print "I want to say something" into irb console if you previously defined the smth = "something" variable. You also may notice the difference between ' and ". So, sometimes you would like to use standalone functions in compass like this:
headings(all) {
color: $color;
}
to compile it into css:
h1, h2, h3, h4, h5, h6 {
color: #2a2a2a;
}
Instead, it throws an error. So, to do this you need to interpolate the call of the function:
#{headings(all)} {
color: $color;
}
And it runs. But if you try to make interpolation with variables:
$color: #abc;
$color2: "#abc";
#mixin some($color) {
color: #{$color};
}
will also throw an error because in this case interpolation outputs a string "#abc".
Try to avoid using ruby syntax in compass if it is possible for cleaner style.
Note: I would appreciate if someone can explain with better compilation details because i'm not a rubyist, i'm pythonist.
Update
Check this code:
#mixin setFonting($from, $to, $size) {
$curr: $from;
$to: $to + 3;
#while $curr != $to {
h#{$curr} {
font-size: $size;
}
$curr: $curr + 1;
$size: $size + 2;
}
}
#include setFonting(1, 3, 20px);
It compiles to :
h1 {font-size: 20px;} h2 {font-size: 22px;} h3 {font-size: 24px;} h4 {font-size: 26px;} h5 {font-size: 28px;}
So as you see, #{} interpolation in compass works outside curly brackets used to define statements for selector.

Resources