Sass/Scss Object Key Value Loop

SASS/SCSS object key value loop

Sass does not currently support mappings. You'll have to live with lists of lists for now.

$icons: star "\2605", lightning "\26A1";

@each $icon in $icons {
$key: nth($icon, 1);
$value: nth($icon, 2);

.icon-#{$key} {
@extend .icon;

&:after {
content: $value;
}
}
}

Loop through nested SCSS list using key/value

As to your update in your question:

Here is a mixin example to your updated requirements.

The example is done in a more general way for multiple use and a good readability.

So font-sizing-settings works as well as margins or even (nearly) every other property setting you would like ot adapt to the breakpoints. And once more with map-element selector it works on simple elements (tags), on classes, id's and more complex selectors as well.

Additional to the $rules map it is based on a breakpoint map. That make sure it works on all breakpoints adviced to the project. But rules to the breakpoints are only added to an element if noted in the rule map ... so you can say: it's an universal swiss knife.

EXAMPLE:

//###### SASS
// this example assumes:
// breakpoints allways min-width ...
$breakpoints: (
sm: 768px,
md: 1024px,
lg: 1280px,
xl: 1400px
);

$rules: (

title: (

selector: 'h1',

all: (
font-family: 'Arial',
font-size: 26px,
line-height: 1.4,
),

sm: (
font-size: 26px,
line-height: 1.4,
),
lg: (
font-size: 80px,
line-height: 1.6,
),
xl: (
font-size: 100px,
),
),

text: (

selector: 'p',

all: (
font-family: 'Courier New',
font-size: 26px,
line-height: 1.4,
),

sm: (
font-size: 16px,
line-height: 1.2,
),
lg: (
font-size: 36px,
line-height: 1.4,
)
),

) !default;

@mixin fontSizing($rule-map, $breakpoints: $breakpoints){

@each $element, $settings-map in $rule-map {

$selector: map-get($settings-map, selector);

// write generel rules
#{$selector} {
@each $property, $value in map-get($settings-map, all){
#{$property}: $value;
}
}

// rules for every breakpoint
@each $breakpoint, $breakpoint-setting in $breakpoints {

// only if breakpoint values set for element
@if map-has-key( $settings-map, $breakpoint ){

// write breakpoints rule
@media ( min-width: #{$breakpoint-setting} ){
#{$selector} {
@each $property, $value in map-get($settings-map, $breakpoint){
#{$property}: $value;
}
}
}

}//if

}//each

}//each

}//mixin

//##### CALL MIXIN
@include fontSizing($rules);

//##### COMPILES TO...

h1 {
font-family: "Arial";
font-size: 26px;
line-height: 1.4;
}

@media (min-width: 768px) {
h1 {
font-size: 26px;
line-height: 1.4;
}
}
@media (min-width: 1280px) {
h1 {
font-size: 80px;
line-height: 1.6;
}
}
@media (min-width: 1400px) {
h1 {
font-size: 100px;
}
}
p {
font-family: "Courier New";
font-size: 26px;
line-height: 1.4;
}

@media (min-width: 768px) {
p {
font-size: 16px;
line-height: 1.2;
}
}
@media (min-width: 1280px) {
p {
font-size: 36px;
line-height: 1.4;
}
}


ADDITIONAL HINT/IMPULSE:

It is possible to compress the rule map. In that case the code working is less, - but more specialised to single tags only and the pre-defined font-sizing settings ... and it is less readable. The general construction of the mixin would be the same but code writing would change a little bit as you work with predefined properties and nested lists instead of nested maps. Feel free to adapt the code. Here is an example for the possible compression to the rule-map:

$rule: (
h1: (
fontFamily: 'Arial',
all: (10px, 1.2),
sm: (12px, 1.4),
lg: (24px, 1.6),
),
p: (
fontFamily: 'Courier New',
all: (8px, 1.2),
sm: (10px, 1.4),
lg: (20px, 1.6),
xl: (24px, 1.8),
),
) !default;

SCSS - Loop a list starting at key 2

You can skip the first entry using map-remove (note I added * 1px) to get unit values :)

This will not mutate the $breakpoints map

@each $key, $value in map-remove($breakpoints, all) {
@media (max-width: $value * 1px){
// dummy content printing out the breakpoint
body::before { content: '#{$key}' }
}
}

@each-loop using map-get( )

You get that error 'cause you are not looping real maps (http://sass-lang.com/documentation/file.SASS_REFERENCE.html#maps).

You could use a nested map to resolve your problem. Something like this:

$names: (
layout-1: (
name: "name-1",
filename: "name-1.jpg",
color: blue
),

layout-2: (
name: "name-2",
filename: "name-2.jpg",
color: red
)
);

@each $key, $value in $names {
.#{map-get($value, name)} {
background-image: url("#{map-get($value, filename)}");
color: map-get($value, color);
}
}

How to get a nested value from a key inside of a @each loop in SCSS Map

I have figured it out with a lot of experimenting, perhaps this will help someone who is looking.

if you change up the map a little bit you can do this.

$character-map: (
billy: (
name: 'billy',
color: $colour-light-blue,
left: (
default: -9px,
mobile: -2px
),
)
);

@each $key, $value in $character-map {
left: map-get(map-get($value, left), default);
}

Will compile to left: 9px;

Sass map loop possibilities

You're referencing the wrong variable. The $item variable (first) references the mapping key name, not the value (second). You need to iterate over the value in your inner loop.

@each $item, $color in $pbcolors {
@each $shade, $value in $color {
.bg-#{$item}-#{$shade} {
background-color: $value;
}
}
}

Loop in Sass @for how to use $#{value}#{$i} correctly?

You can set a variable of $menu-icon-items:"\f007","\f07c","\f1fa"; and loop through using its length, see example here:

HTML

<div class="menu-item1">1</div>
<div class="menu-item2">2</div>
<div class="menu-item3">3</div>

SCSS

$menu-icon-items:"\f007","\f07c","\f1fa";
@for $i from 1 through length($menu-icon-items) {
.menu-item#{$i}{
&:before {
content:nth($menu-icon-items,$i);
}
}
}

SCSS / SASS : How to create variables inside an each loop

As Flying mentioned in the comments Sass does not support dynamic variables creation.
I think I would use a function to return the CSS variable if found in the $colors map

$colors : (
"pink" : #E20071,
"blue" : #00A3DA,
"gray" : #939394,
"darkGray" : #939394,
"yellow" : #FEA347,
"green" : #4CA66B,
"white" : #FFFFFF,
"black" : #1B1B1B,
);

:root{
@each $key, $value in $colors {
--#{$key} : #{$value};
}
}

@function color($name){
@if not map-get($colors, $name+''){
@error "Color `#{$name}` not found in map $colors";
}
@return var(--#{unquote($name)});
}

.class-name {
color: color(pink); // var(--pink);
color: color(nope); // throws error: "Color `nope` not found in map $colors"
}

// note! we stringify $name (the +'' part) to ensure Sass does not interpret
// it as a color – e.g. pink represents the hex value #ffc0cb

Access Key, Value in Sass Map

You can do it using nth function, like this:

:root {
@each $name, $value in $font-scale {
--font-size-scale-#{$name}: #{nth($value,1)};
--line-height-#{$name}: #{nth($value,2)};
}
}

SASS loop on map, get next iteration

Here you can find your solution: https://github.com/elcheio/sass-map-get-next-prev

Using that function (i.e. map-get-next function) you can resolve your problem.

So, first of all copy and paste that function... very very simple (^_^;)

@function map-get-next($map, $key, $fallback: false, $return: value) {
// Check if map is valid
@if type-of($map) == map {
// Check if key exists in map
@if map-has-key($map, $key) {
// Init index counter variable
$i: 0;
// Init key index
$key-index: false;
// Traverse map for key
@each $map-key, $map-value in $map {
// Update index
$i: $i + 1;
// If map key found, set key index
@if $map-key == $key {
$key-index: $i;
}
// If next index return next value or key based on $return
@if $i == $key-index + 1 {
@if $return == key {
@return $map-key;
} @else {
@return $map-value;
}
}
// If last entry return false
@if $i == length($map) {
@return $fallback;
}
}
@warn 'No next map item for key #{$key}';
@return $fallback;
}
@warn 'No valid key #{$key} in map';
@return $fallback;
}
@warn 'No valid map';
@return $fallback;
}

Then you can add your map:

$colors: (
'primary': #aaa,
'secondary': #bbb,
'color-3': #ccc,
'color-4': #ddd,
'color-5': #eee,
);

In the end, you have to create an @each loop to estract every pair name/value of your map:

@each $name, $value in $colors{
.btn-#{$name} {
background-color: $value;
&:hover{
background-color: map-get-next($colors, $name, #ffffff); // <== here you have to write the color for your last item (i.e. 'color-5'; in this example is white)
}
}
}

That's it! Your output will be:

.btn-primary {
background-color: #aaa;
}
.btn-primary:hover {
background-color: #bbb;
}

.btn-secondary {
background-color: #bbb;
}
.btn-secondary:hover {
background-color: #ccc;
}

.btn-color-3 {
background-color: #ccc;
}
.btn-color-3:hover {
background-color: #ddd;
}

.btn-color-4 {
background-color: #ddd;
}
.btn-color-4:hover {
background-color: #eee;
}

.btn-color-5 {
background-color: #eee;
}
.btn-color-5:hover {
background-color: #ffffff;
}


Related Topics



Leave a reply



Submit