generate classes from Scss / Sass nested map - sass

How does one generate a list of classes for each key that has a single value inside a Scss/Sass map?
For example, from this Scss map (disregard nested naming conventions, this will be used for many maps of varying purposes):
(See Codepen: http://codepen.io/harlanlewis/pen/emWVrr (thanks cimmanon!))
$palette: (
brown: hsl(33,35,50),
blue: hsl(207,80,50),
green: (
0: hsl(157,65,65),
1: hsl(157,50,50),
alt: (
0: hsl(125,65,65),
),
),
red: (
0: hsl(0,60,50),
alt: (
0: hsl(0,100,50),
),
),
yellow: (
0: hsl(50,100,60),
1: hsl(50,100,100),
),
};
-
#mixin map-to-class($map, $selector: '', $property: '') {
$selector: if($selector == '' and &, &, $selector);
#each $key, $value in $map {
#if type-of($value) == map {
$selector: selector-append($selector, #{$key});
#include map-to-class($value, $selector, $property) {
#content;
}
} #else {
#at-root #{$selector}#{$key} {
#{$property}: $value;
};
};
};
};
#include map-to-class($palette, '.u-fg__', 'color')
...desired classes to generate:
.u-fg__brown { color: hsl(33,35,50) }
.u-fg__blue { color: hsl(207,80,50) }
.u-fg__green0 { color: hsl(157,65,65) }
.u-fg__green1 { color: hsl(157,50,50) }
.u-fg__greenalt0 { color: hsl(125,65,65) }
.u-fg__red0 { color: hsl(0,60,50) }
.u-fg__redalt0 { color: hsl(0,100,50) }
.u-fg__yellow0 { color: hsl(50,100,60) }
.u-fg__yellow1 { color: hsl(50,100,80) }
The actual (incorrect) generated classes are: (note greenredyellow instead of just yellow)
... (brown, blue, and green are fine) ...
.u-fg__green0 { color: hsl(157,65,65) }
.u-fg__green1 { color: hsl(157,50,50) }
.u-fg__greenalt0 { color: hsl(125,65,65) }
.u-fg__greenred0 { color: hsl(0,60,50) }
.u-fg__greenredalt0 { color: hsl(0,100,50) }
.u-fg__greenredyellow0 { color: hsl(50,100,60) }
.u-fg__greenredyellow1 { color: hsl(50,100,80) }

What you're looking for is a recursive mixin. Walk through the mapping. If the value is a mapping, call itself otherwise print out the property/value.
$palette: (
'brown': hsl( 33, 35, 50),
'blue': hsl(207, 80, 50),
'green': (
0: hsl(157, 65, 65),
1: hsl(157, 50, 50),
alt: (
0: hsl(125, 65, 65),
),
),
'red': (
0: hsl(0, 60, 50),
alt: (
0: hsl(0, 100, 50),
),
),
'yellow': (
0: hsl(50, 100, 60),
2: hsl(50, 100, 100),
),
);
#mixin map-to-class($map, $property, $sel, $divider: '') {
$sel: if($sel == '' and &, &, $sel);
#debug $sel;
#{$sel} {
#each $k, $v in $map {
#at-root #{$sel}#{$divider}#{$k} {
#if type-of($v) == map {
#include map-to-class($v, $property, '', $divider) {
#content;
}
} #else {
#{$property}: $v;
}
}
}
}
}
#include map-to-class($palette, color, '.u-fg__', '');
Output:
/* line 33, ../sass/test.scss */
.u-fg__brown {
color: #ac8453;
}
/* line 33, ../sass/test.scss */
.u-fg__blue {
color: #198ae6;
}
/* line 33, ../sass/test.scss */
.u-fg__green0 {
color: #6ce0b3;
}
/* line 33, ../sass/test.scss */
.u-fg__green1 {
color: #40bf8e;
}
/* line 33, ../sass/test.scss */
.u-fg__greenalt0 {
color: #6ce075;
}
/* line 33, ../sass/test.scss */
.u-fg__red0 {
color: #cc3333;
}
/* line 33, ../sass/test.scss */
.u-fg__redalt0 {
color: red;
}
/* line 33, ../sass/test.scss */
.u-fg__yellow0 {
color: #ffdd33;
}
/* line 33, ../sass/test.scss */
.u-fg__yellow2 {
color: white;
}
Note that I quoted your mapping key names. Sass will convert those to their hex code equivalents under certain compression types.

Related

SASS mixin with if statements to handle conditions

I was writing a mixin in SCSS to handle a map (padding, margin, etc etc), but I am getting a little stuck with all the #if logic to get the proper output.
In my defaults.scss file I have 2 variable, $size and $prefixes:
$size: auto, 1, 2, 2\.5, 4;
$prefixes: (
padding: (
top: "pt",
right: "pr",
bottom: "pb",
left: "pl",
x: "px",
y: "py",
all: "p",
),
);
I created mixin.scss that would take in $sizes, $prefixes, and a $property (ie "padding") like:
#include mixer.create-space(
$sizes: $sizes,
$prefixes: $padding,
$property: "padding"
);
The $padding in this case was just assigned earlier with map.get like
$padding: map-get($prefixes, "padding");
I know I need to #each through the $sizes and #each through the $prefixes to assign the right values, but I am really getting lost in my conditions.
px and py need 2 properties each (left/right || top/bottom)
if $size == auto, only assign to px/py (Currently it is being added to every class)
#mixin create-space($sizes, $prefixes, $property) {
// $size => 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96
#each $size in $sizes {
// $alignment => "top", "bottom", "left", "right", "y", "x", "all"
// $prefix => "pt", "pr", "pb", "pl", "py", "px", "p"
#each $alignment, $prefix in $prefixes {
// If $alignments is x, then $property-left and $property-right
// If $alignments is y, then $property-top and $property-bottom
// If $alignments is all, then $property
// If $size is auto for $alignment top, right, bottom, left, then $property: auto
#if $alignment == "x" {
.#{$prefix}-#{$size} {
#{$property}-left: calc(($size * 4) / 16) + rem;
#{$property}-right: calc(($size * 4) / 16) + rem;
}
} #else if $alignment == "y" {
.#{$prefix}-#{$size} {
#{$property}-top: $size * 4;
#{$property}-bottom: calc(($size * 4));
}
} #else if $size == "auto" {
.#{$prefix}-#{$size} {
#{$property}: auto;
}
} #else if $alignment == "all" {
.#{$prefix}-#{$size} {
#{$property}: calc(($size * 4) / 16) + rem;
}
} #else {
.#{$prefix}-#{$size} {
#{$property}-#{$alignment}: calc(($size * 4) / 16) + rem;
}
}
}
}
}
My mixin does work, but I run into errors when I am trying to calc the $size into rems when I run into $size: auto etc. Really, it is just becoming a mess.
** Edit **
It really seems that the decimal numbers were throwing everything off in the long run.

How to pass multiple content blocks as arguments to a mixin?

I wanna be able to do something like so:
.myTransitionableElement {
transition: all .5s;
.subChild { transition: all 1s }
#include transitionKeyframes(
start: {
opacity: 0;
transform: tranlsate(50px);
.subChild {
transform: rotate(45deg);
}
},
end: {
opacity: 1;
transform: tranlsate(0);
.subChild {
transform: rotate(0);
}
}
)
}
#mixin transitionKeyframes($args) {
&.transitionStartKeyframe {
#include map_get($args, "start");
}
&.transitionEndKeyframe {
#include map_get($args, "end");
}
}
Which should at the end be equivalent to:
.myTransitionableElement {
transition: all .5s;
.subChild { transition: all 1s }
&.transitionStartKeyframe {
opacity: 0;
transform: tranlsate(50px);
.subChild {
transform: rotate(45deg);
}
}
&.transitionEndKeyframe {
opacity: 1;
transform: tranlsate(0);
.subChild {
transform: rotate(0);
}
}
}
The reason behind this is that I wanna find a way to not have to remember those classes names every time I use a JS abstraction that uses those classes.
You can't pass different content as arguments in SASS mixins.
Below is example of what you can achieve using SASS.
Can you specify what you want to achieve here?
#mixin rtl() {
&.ltr {
#content;
direction: ltr;
}
&.rtl {
#content;
direction: rtl;
}
}
.parent {
#include rtl {
display: flex;
}
}
You can try different approaches here. In this particular one I'm using map to hold the data for me. But if attributes and class names are fixed; you can even simplify it.
$map: (Start: (self: (opacity: '0', transform: 'tranlsate(50px)'), child: (transform: rotate(45deg))), End: (self: (opacity: '1', transform: 'tranlsate(0)'), child: (transform: rotate(0))));
$prefix: transition; $postfix: Keyframe;
#mixin transitionKeyframes($map) {
#each $key, $val in $map {
&.#{$prefix}#{$key}#{$postfix} {
#each $attr, $prop in map-get($val, self) {
#{$attr}: #{$prop};
}
.subChild {
#each $attr, $prop in map-get($val, child) {
#{$attr}: #{$prop};
}
}
}
}
}
.myTransitionableElement {
#include transitionKeyframes($map);
transition: all .5s;
.subChild { transition: all 1s }
}
Use mixins for a repetitive task. Modify the mixin or $frames if you have many variations. Breakdown opacity and transistion further to smaller mixins and invoke them conditionally.
$prefix: transition; $postfix: Keyframe;
$frames: 'Start', 'End';
$opcity-main: ('Start': 0, 'End': 1);
$translate-main: ('Start': 50, 'End': 0);
#mixin transitionKeyframes($frame) {
#each $key in $frame {
&.#{$prefix}#{$key}#{$postfix} {
$opacity: map-get($opcity-main, $key);
$translate: map-get($translate-main, $key);
opacity: $opacity;
transform: tranlsate($translate + px);
}
}
}
.myTransitionableElement {
#include transitionKeyframes($frames);
transition: all .5s;
.subChild { transition: all 1s }
}

How can I get responsive classes with this mixin

My sass file looks like this when I use my mixin:
$viewports: 25 50 75 100;
#each $viewport in $viewports {
.vh-#{$viewport} {
height: #{$viewport}vh;
}
}
.vh-25 {
height: 25vh;
}
...
But I want that class and the class with the #sm, #md, …
Can I do that with sass?
#media (min-width: 576px) and (max-width: 767px) {
.vh-25#sm {
height: 25vh;
}
}
# is not allowed in class name, but if you want to replace it with -:
$viewports: (
xs: 25,
sm: 50,
md: 75,
xl: 100,
);
#each $viewport, $height in $viewports {
.vh-#{$height}-#{$viewport} {
height: #{$height}vh;
}
}

SASS check if current loop contains part of input

I have an array called $ratings-list that I'm trying too loop through and if the $current-class contains .5 then to use a a segment of css otherwise use a different segment;
$ratings-list: 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5;
#each $current-class in $ratings-list {
$i: index($ratings-list, $current-class);
&[data-rating="#{$current-class}"] {
#if (index($current-class, .5)) {
.rating-stars__star:nth-child(-n+#{floor($current-class)}) .ratings-star {
height: floor($current-class);
}
} #else {
.rating-stars__star:nth-child(-n+#{$i}) .ratings-star {
height: 7px;
}
}
}
}
The above always returns the else height: 7px segment.
You could turn $current-class into a string and use str-index instead.
$ratings-list: 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5;
#each $current-class in $ratings-list {
$i: index($ratings-list, $current-class);
[data-rating="#{$current-class}"] {
#if (str-index(#{"" + $current-class}, '.5')) {
.rating-stars__star:nth-child(-n+#{floor($current-class)}) .ratings-star {
height: floor($current-class);
}
} #else {
.rating-stars__star:nth-child(-n+#{$i}) .ratings-star {
height: 7px;
}
}
}
}
Compiles to
[data-rating="1"] .rating-stars__star:nth-child(-n+1) .ratings-star {
height: 7px;
}
[data-rating="1.5"] .rating-stars__star:nth-child(-n+1) .ratings-star {
height: 1;
}
[data-rating="2"] .rating-stars__star:nth-child(-n+3) .ratings-star {
height: 7px;
}
[data-rating="2.5"] .rating-stars__star:nth-child(-n+2) .ratings-star {
height: 2;
}
[data-rating="3"] .rating-stars__star:nth-child(-n+5) .ratings-star {
height: 7px;
}
[data-rating="3.5"] .rating-stars__star:nth-child(-n+3) .ratings-star {
height: 3;
}
[data-rating="4"] .rating-stars__star:nth-child(-n+7) .ratings-star {
height: 7px;
}
[data-rating="4.5"] .rating-stars__star:nth-child(-n+4) .ratings-star {
height: 4;
}
[data-rating="5"] .rating-stars__star:nth-child(-n+9) .ratings-star {
height: 7px;
}

How to create list of vars in loop?

I have a main colour #abc
and we can create colour with darken with percentage
how can i create list of darken colours with a loop?
#mixin create-color($main) {
create some vars
}
#include create-color(blue);
i will get
$c-green-1: green
$c-green-2: (green-lighten 10%)
$c-green-3: (green-lighten 20%)
This will accomplish what you're after:
#mixin alphaColor($name,$color) {
#for $i from 1 through 10 {
.c-#{$name}-#{$i} {
color: rgba($color,$i/10);
}
}
}
#include alphaColor("blue",blue);
#include alphaColor("red",#ed1414);
#include alphaColor("absurd",rgb(20,237,20));
This is a mixin that will take a color (named, hex, rgb), and loop thru it creating named (you give it an initial name) color declarations in RGBA with alpha from .1 - 1 incrementing by .1.
Output above would be:
.c-blue-1 { color: rgba(0, 0, 255, 0.1); }
.c-blue-2 { color: rgba(0, 0, 255, 0.2); }
.c-blue-3 { color: rgba(0, 0, 255, 0.3); }
.c-blue-4 { color: rgba(0, 0, 255, 0.4); }
.c-blue-5 { color: rgba(0, 0, 255, 0.5); }
.c-blue-6 { color: rgba(0, 0, 255, 0.6); }
.c-blue-7 { color: rgba(0, 0, 255, 0.7); }
.c-blue-8 { color: rgba(0, 0, 255, 0.8); }
.c-blue-9 { color: rgba(0, 0, 255, 0.9); }
.c-blue-10 { color: blue; }
.c-red-1 { color: rgba(237, 20, 20, 0.1); }
.c-red-2 { color: rgba(237, 20, 20, 0.2); }
.c-red-3 { color: rgba(237, 20, 20, 0.3); }
.c-red-4 { color: rgba(237, 20, 20, 0.4); }
.c-red-5 { color: rgba(237, 20, 20, 0.5); }
.c-red-6 { color: rgba(237, 20, 20, 0.6); }
.c-red-7 { color: rgba(237, 20, 20, 0.7); }
.c-red-8 { color: rgba(237, 20, 20, 0.8); }
.c-red-9 { color: rgba(237, 20, 20, 0.9); }
.c-red-10 { color: #ed1414; }
.c-absurd-1 { color: rgba(20, 237, 20, 0.1); }
.c-absurd-2 { color: rgba(20, 237, 20, 0.2); }
.c-absurd-3 { color: rgba(20, 237, 20, 0.3); }
.c-absurd-4 { color: rgba(20, 237, 20, 0.4); }
.c-absurd-5 { color: rgba(20, 237, 20, 0.5); }
.c-absurd-6 { color: rgba(20, 237, 20, 0.6); }
.c-absurd-7 { color: rgba(20, 237, 20, 0.7); }
.c-absurd-8 { color: rgba(20, 237, 20, 0.8); }
.c-absurd-9 { color: rgba(20, 237, 20, 0.9); }
.c-absurd-10 { color: #14ed14; }
SASSmeister demo: http://www.sassmeister.com/gist/39577ef556de22a9acfe934cbc108a4e

Resources