Function to dynamically create classes from lists - sass

Im fairly new to SASS and I'm confused by the way lists work. I have a multidimensional list like this:
$stylethemes: {
(15, bold, red),
(12, normal, blue)
}
I now want a function that makes a class for each list in $stylethemes and then puts the values of that list into that class. The output I want from the above list is this:
.theme1{
font-size: 15;
font-weight: bold;
color: red;
}
.theme2{
font-size: 12;
font-weight: normal;
color: blue;
}
Can anyone show me how I can do this? Thanks in advance.

The code to produce the desired results would look like this:
$stylethemes: (
(15, bold, red),
(12, normal, blue)
);
#for $i from 1 through length($stylethemes) {
.theme#{$i} {
font-size: nth(nth($stylethemes, $i), 1);
font-weight: nth(nth($stylethemes, $i), 2);
color: nth(nth($stylethemes, $i), 3);
}
}
However, you'll find this isn't particularly flexible. You're better off using mappings for the property/values so that you don't have to guarantee a specific order:
$stylethemes: (
(font-size: 15, font-weight: bold, color: red),
(font-size: 12, font-weight: normal, color: blue)
);
#for $i from 1 through length($stylethemes) {
.theme#{$i} {
#each $prop, $val in nth($stylethemes, $i) {
#{$prop}: $val;
}
}
}
Or
$stylethemes: (
theme1: (font-size: 15, font-weight: bold, color: red),
theme2: (font-size: 12, font-weight: normal, color: blue)
);
#each $theme, $properties in $stylethemes {
.#{$theme} {
#each $prop, $val in $properties {
#{$prop}: $val;
}
}
}

Youa re basically asking us to solve your pboelm, but fine, since SASS is very deep and fun to use and can be a bit daunting with its lack of map looping functions. I changed a couple of things but this is essentially it:
// first off, I decided to make your style themes a SASS map. This is useful because your
// your theme will be intricately linked to its name, making it easier to read
// you could to the same with the values, but for now I'll count them.
$stylethemes: (
theme-1 : (15, bold, red),
theme-2 : (12, normal, blue)
);
// first, we need to create a regular list we can loop through with a for loop
// map-keys returns a list we can use for that
$allthemes : map-keys($stylethemes);
// then we can run through all the themes by finding the theme name from the above list
#for $var from 1 through length($allthemes) {
// heres how we get the theme name
$theme : nth($allthemes, $var);
// heres how we get the values stored in your SASS map
$this : map-get($stylethemes, $theme);
// then I assign all your variables to vars, but its not necessary
$size : nth($this, 1);
$style : nth($this, 2);
$color : nth($this, 3);
// now print the theme name as a classname
.#{$theme}{
// then print the values - you could also use the above nth($this, n) to get them.
font-size: $size;
font-weight: $style;
color: $color;
}
}
I got all the function info from the SASS documentation site: http://sass-lang.com/documentation/Sass/Script/Functions.html, so have a look around there, there is a dedicated section for lists and maps. Have a look at lists and maps as they will be very useful for this kind of thing.

Related

Count up with #each from a list with Sass

I’m wondering if I can add a ‘counter’ with Sass when using the #each function. I have a list of backgrounds colors and I want to use the #each function to output a class name and a number behind it.
Is there a way with Sass to automatically count and add that into the class name?
$colors: blue, red, green;
#each $color in $colors {
.class- {
background: $color;
}
}
So the output would be:
.class-1 {
background: red
}
.class-2 {
background: blue
}
Etc…
This did the trick for me:
#for $i from 1 through length($colors) {
.class-#{$i} {
background: '#{nth($colors, $i)}';
}
}

Sass manipulate with #content

Is it possible to manipulate with #content magic variable in SASS?
I would like to replace some stuff in here before output.
Or maybe can I fill some variable with it?
The conclusion is that, I want to make an mixin #important that create both versions. Important, and no-important.
Input
.test {
#include important {
color: red;
text-align: left;
}
}
Expected output
.test {
color: red;
text-align: left;
}
.test-i {
color: red !important;
text-align: left !important;
}
No, you can't. But I quickly wrote you a mixin to make it work. It doesn't accepts multiple properties (yet).
First Note: I changed the mixin it now does accept multiple properties. Here is the Codepen.
Second Note: I updated the mixin adding multiple properties does no longer compile to different classes for each property, instead you get two versions, one without the !important suffix and one with.
This is the mixin:
#function return($state) {
#return if($state == '', '', '-i');
}
#mixin loop($name, $items...) {
#each $item in $items / 2 {
#each $state in ('', '!important') {
$suffix: return($state);
.#{$name}#{$suffix} {
#for $i from 1 through (length($items) / 2) {
#{nth($items, ($i * 2) - 1)}: #{nth($items, ($i * 2))} #{$state};
}
}
}
}
}
This is how you include it:
// #include loop([classname], [property], [value]);
#include loop(whateverClassname, color, red);
This is what it compiles to:
.whateverClassname {
color: red ;
}
.whateverClassname-i {
color: red !important;
}
This is what it now compiles to, when you use multiple properties at once:
#include loop(whateverClassname, color, red, background-color, green, display, flex);
.whateverClassname {
color: red ;
background-color: green ;
display: flex ;
}
.whateverClassname-i {
color: red !important;
background-color: green !important;
display: flex !important;
}
Conclusion: it works as expected and does no longer bloat your CSS.
Hope I could help you at least a little ;-)

Scss modify rules based on topmost parent

Let's propose that we have the following scss code:
.a {
.b {
.c {
// rules here
}
}
}
This will output the following css: .a .b .c
Now, if I would like to modify the ruleset of .c if .a gets another class, I would have to modify my scss like this:
.a {
.b {
.c {
// rules here
}
}
&.d {
.b {
.c {
// rules here
}
}
}
}
If there are multiple modifications, my scss will become very long, and the rules refering to .c will be out and apart. Isn't there an easier syntax, to keep all the rules of .c together in the code, so that when I need to modify something, it will be easier? I am thinking about something along these lines:
.a {
.b {
.c {
// rules here
^^&.d {
// go up 2 selectors (hence twicte the ^), and add to the selector the `.d` class, then keep on modifying the current selector `.c` here
}
}
}
}
EDIT: I am used to specify each element (or most, any case), in the selector path, so my selectors are as explicit as possible. The above example can be reflected for a dashboard button, which is deep inside the dashboard, and with the basic approach, I will have to replicate 5 levels of nesting, to specify some style for a button, based on the status of the dashboard (ie. sidebar-open). I could of course add a class directly to the button and style it like that, but imo that's an overkill, as there are a lot of different things that change in the stylesheet for this specific status.
For a complete example, when I click this specific button, the sidebar should pop out, some other elements that are getting overlapped, should be pushed to the right, etc. My javascript, simply adds the status representing class to the whole dashboard, and I would like to style everything accordingly. But this way, the styles for the different elements are far and apart unfortunately.
You can use the
sass variable to structure your nested classes and styles.
#each rule to iterate over every key/value pair to emit styles.
Let's suppose you can have 3 classes on first level i.e x, y, z, with their child classes a, b.
Then your scss variable code will look like below:
$classes: (
x: (
a: (
color: blue,
font-size: 15px
),
b: (
color: blue,
font-weight: 600
)
),
y: (
a: (
color: red,
),
b: (
color: blue,
font-size:20px
)
),
z: (
a: (
color: black,
overflow: scroll
),
b: (
color: black,
font-size: 20px,
background-color: white
)
)
);
Now we can iterate above variable like below:
#each $key, $value in $classes {
.#{$key} {
#each $key1, $value1 in $value {
.#{$key1} {
#each $key2, $value2 in $value1 {
#{$key2}: $value2
}
}
}
}
}
If in case you want to add more classes in the future, then you need follow the same structure and add class names and styles accordingly.
Result (CSS)
.x .a {
color: blue;
font-size: 15px;
}
.x .b {
color: blue;
font-weight: 600;
}
.y .a {
color: red;
}
.y .b {
color: blue;
font-size: 20px;
}
.z .a {
color: black;
overflow: scroll;
}
.z .b {
color: black;
font-size: 20px;
background-color: white;
}
I hope it solves your problem & beautifies your code as well.

Stripping variables of hashtag in SCSS before using them in selectors?

Another problem here.. my team wants to use borders in different widths, colors and positions. So, I made this:
$position-list: top right bottom left;
$colors-list: fff ccc ddd eee;
#for $i from 1 through 3 {
#each $position in $position-list {
#each $color in $colors-list {
.border-#{$position}-#{$i}-#{$color} {
border-#{$position}: #{$i}px solid #{"#"}#{$color} !important;
}
}
}
}
This works great, however, I want to include the colors as variables from my colors.scss sheet ($light-color, $dark-color etc). The problem is that the hashtags from the colors.scss sheet will be transfered as well ($dark-color: #000), so it will most likely generate a weird selector (.border-top-1-#000) or doesn't compile at all.
Is there a way of stripping the variables from the colors.scss sheet of their hashtags before putting them in the selector? Or does anyone have a different/better approach?
We can convert the color to a string (#inspect) and slice it (#str-slice).
$dark-color: #000;
$light-color: #fff;
$abc-color: #abc;
$position-list: top right bottom left;
$colors-list: $dark-color $light-color $abc-color;
#for $i from 1 through 3 {
#each $position in $position-list {
#each $color in $colors-list {
$stripped-color: str-slice(inspect($color), 2);
.border-#{$position}-#{$i}-#{$stripped-color} {
border-#{$position}: #{$i}px solid #{$color} !important;
}
}
}
}
Output (example):
.border-top-1-abc {
border-top: 1px solid #abc !important;
}

Using a sass variable in an #each loop

I'm trying to create a little overview for all the colors we use in our corporate identity. All our colors have been defined in _settings-colors.scss, and the only reason I need this bit of css is for the library, where the colors need to be listed.
What I have now is as follows:
$colors-brand: color-brand, color-brand-40, color-brand-60, color-brand-70;
.prfx-color {
display: block;
height: 5rem;
width: 100%;
#each $color in $colors-brand {
&--#{$color} {
background-color: #{'$'+$color};
&::after {
content: '$'+$color;
}
}
}
}
These color-brand variables are set in another file which I'm including in this scss file.
The code above outputs this:
.prfx-color {
display: block;
height: 5rem;
width: 100%;
}
.prfx-color--color-brand {
background: $color-brand;
}
.prfx-color--color-brand::after {
content: "$color-brand";
} [...etc]
What I'm after however, is this:
.prfx-color--color-brand {
background: #00ff11; // don't worry, brand is not actually this color
}
The problem I'm having is that the $color-brand variable isn't interpreted as a sass variable anymore, but is a literal value. I need the #hheexx that this variable refers to!
All the solutions I've found so far consist of using two lists, or a key-value pair. In my situation these variables have already been set once, and I want a solution where I don't want to have to manually edit the library if the colors change.
Is this at all possibe, or am I too greedy here?
And I realized I overcomplicated it. You don't need any extra functions because the #each is designed to work with maps and iterating over multiple values.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $key, $val in $colors {
&--#{$key} {
background-color: $val;
&::after { content: "$#{$key}"; }
}
}
}
You could use a map.
Here's a sassmeister playground for you.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $color in map-keys($colors) {
&--#{$color} {
background-color: map-get($colors, $color);
&::after { content: "$#{$color}"; }
}
}
}

Resources