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
CSS Pseudo Class for Leaving Hover
How to Add Multiple Classes to Markdown Using Jekyll
Bootstrap Modal Restores Button Focus on Close
Firefox Add's 2Px Padding in a Submit Button
Using CSS Clip with Percentage
Image Right Edge Fade/Blur CSS
How to Make a Horizontally Scrollable Cell in a Datatable
How to Control Height of Ellipse in Radial Gradient
How to Test CSS Properties for a React Component Using React-Testing-Library
Configuring Compass on Windows
Fill Element with Slanted Background on Hover
Absolutely Positioned Flexbox Doesn't Expand to Fit Contents
CSS3 Background-Size Cover to Percentage Animation Zoom