Multiple maps, one loop Sass - css

I have two different maps in my _variables.scss file:
$breakpoints: (
bkp-320: 320px,
bkp-480: 480px,
bkp-768: 768px,
bkp-992: 992px,
bkp-1200: 1200px
);
$fontsizes: (
tiny: (
null: 12px,
bkp-768: 11px
),
small: (
null: 14px,
bkp-768: 13px
),
base: (
null: 16px,
bkp-768: 15px
),
large: (
null: 18px,
bkp-768: 17px
)
);
I would combine them in a "#mixin / function font-size" and recall it in my scss:
.selector{
#import font-size(small);
}
to produce in my css something like this:
.selector {
/* null: default value of font-size, not need mediaqueries */
font-size: 14px;
}
#media only screen and (min-width: 768px) {
.selector {
font-size: 13px;
}
}
thxxx

you could try to change $fontsizes map with this structure
small: (
default: 14px,
resolutions:(
(
breakpoint: 'bkp-768',
default: 13px
),
(
breakpoint: 'bkp-480',
default: 20px
)
)
),
and call mixin font-size with a key
#function getMap($map,$key) {
#if map-has-key($map, $key) {
#return map-get($map, $key);
}
#warn "Unknown `#{$key}` in $map.";
#return null;
}
// font-size
#mixin font-size($key){
$fontsize: getMap($fontsizes, $key);
$resolutions: getMap($fontsize, resolutions);
font-size: getMap($fontsize, default);
#each $resolution in $resolutions {
#media only screen and (min-width: getMap($breakpoints,getMap($resolution, breakpoint)) ) {
font-size: getMap($resolution, 'default');
}
};
}
p {
#include font-size(small);
}
h1 {
#include font-size(large);
}

Related

how can i use #custom-media in sass mixins?

i am trying to generate #custom-media entries in a mixin, sourced from a sass map, but get the error that the media query is not a valid css value
eg
#use 'sass:map';
#mixin custom-media($breakpoints) {
$breakpoint-names: map.keys($breakpoints);
#each $breakpoint in $breakpoint-names {
$media-query: null;
$min: map.get($breakpoints, $breakpoint, width, min);
$max: map.get($breakpoints, $breakpoint, width, max);
#if ($min) { $min: (min-width: #{$min}); }
#if ($max) { $max: (max-width: #{$max}); }
#if ($min and $max) {
$media-query: "#{$min} and #{$max}";
}
#else {
$media-query: $min or $max;
}
#custom-media --#{breakpoint}-media-query #{$media-query};
}
}
.root {
#include custom-media((
small: (
width: (
max: 40em,
)
),
medium: (
width: (
min: 40em,
max: 80em,
)
),
large: (
width: (
min: 80em,
)
)
))
};
the error is (max-width: 40em) is'nt a valid css value., despite that being exactly what i want the value to be. does sass not support the #custom-media syntax?
A mixin can return only a valid CSS rules.
#custom-media ... It's not a valid CSS rule.
If you want customize breakpoints points, create a CSS var.

Loop through nested SCSS list using key/value

I have a nested scss list with some font definitions:
$font-cinzel: "Cinzel", serif;
$font-poppins: "Poppins", sans-serif;
$fonts: (
"title": (
"all": (
font-family: $font-cinzel
),
"sm": (
font-size: 26px,
line-height: 1.4
),
"lg": (
font-size: 80px,
line-height: 1.6
)
),
"text": (
"all": (
font-family: $font-poppins
),
"sm": (
font-size: 16px,
line-height: 1.2
),
"lg": (
font-size: 36px,
line-height: 1.4
)
)
);
Now I would like to write a mixin and a function to generate a styling block when passing the font name for each viewport. So all styles defined in all should be rendered outside of a media query (rule for all viewports to avoid redundancy). All other style which are unique for the viewport are defined in the viewport block e.g. all small styles are in sm, all large styles with different values in lg etc. I also have a breakpoint mixin, where I map the min-width by using the viewport name form the font list:
breakpoint mixin:
$breakpoints: (
sm: 768px,
md: 1024px,
lg: 1280px,
xl: 1400px
);
#function get-bp($bp) {
#if $bp {
$bp: map-get($breakpoints, $bp);
} #else {
#error "Parameter #{$vp} is unknown or empty.";
}
#return $bp;
}
This is how I try to loop the stuff:
#function get-value($definition, $vp) {
#each $prop, $val in map-get($definition, $vp) {
#return $val;
}
}
#mixin font($name) {
$definition: map-get($fonts, $name);
#each $key, $value in $definition {
#if $key == "all" {
$prop: get-value($definition, $key);
} #else {
#media (min-width: get-bp($key)) {
$prop: get-value($definition, $key);
}
}
}
}
h1 {
#include font('title');
}
p {
#include font('text');
}
Expected output:
h1 {
font-family: 'Cinzel', serif;
#media (min-width: 768px) {
font-size: 26px;
line-height: 1.4;
}
#media (min-width: 1280px) {
font-size: 80px;
line-height: 1.6;
}
}
p {
font-family: 'Poppins', sans-serif;
#media (min-width: 768px) {
font-size: 16px;
line-height: 1.2;
}
#media (min-width: 1280px) {
font-size: 36px;
line-height: 1.4;
}
}
I don't get any errors, but the styles are not available for the elements.
Here is the codepen: https://codepen.io/STWebtastic/pen/QWdOZPN
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;
There are some huddles in your code ...
The mixin uses an #each loop ... which is not needed but malicious as you don't want to write all classes at once but to use it to include only a special group of properties to a single class/selector (in your example you try to advice to h1 {...} only.
The error:
In your loop the mixins calls every element in map-get($fonts, $name)' which in your example is inner map title`.
Now for every element of this map (= fontFamily, sm, lg) it should do something ...
In your case you assume all elements are maps to get a value from it.
So the loop takes the first element fontFamily
There you try to get the value for a inner element fontFamily ...
SASS first checks the funciton map-get and realize fontFamily you named as map for the function is not a map ...
At that point SASS and ends ...
... and you are in trouble.
What you may do:
Reorganize your map. You can do it easier: only store the values.
In your mixin call direct the value you need. No loop necessary! You only need two special values which you can call targeted.
And don't forget to advice a line height which fits your new font-size ;-)
Code could look like this:
// ### SASS
$fonts: (
title: (
fontFamily: 'Arial',
sm: 12px,
lg: 24px,
),
text: (
fontFamily: 'Courier New',
sm: 10px,
lg: 20px,
),
) !default;
#mixin font( $item, $size ) {
font-family: map-get(map-get($fonts, $item), fontFamily );
font-size: map-get(map-get($fonts, $item), $size );
line-height: 1.2em;
}
h1 {
#include font( title, sm);
}
// ### Compiles to CSS
h1 {
font-family: "Arial";
font-size: 12px;
line-height: 1.2em;
}

How to change sass import file when body class is changing

Is any way to change scss file with color variables when class in body is changing?
I mean when I have in html:
<body class="custom"> ... </body>
I want to load in style.scss
#import 'custom';
and when I have
<body class="dark-mode"> ... </body>
I want to load in style.scss
#import 'dark-mode';
You can not make an #import depending on a condition, but there is a ton of possible other approaches to this. Here is a small framework I wrote back then.
#function keyFilter($iKey, $fKey, $rKey) {
#if ($iKey == $fKey) {
#return $rKey;
}
#return $iKey;
}
#function v($key) {
#return var(--#{$key});
}
//
$modes: (
"&": (
"color": #000,
),
"dark": (
"color": #fff,
),
);
//
#each $key, $map in $modes {
body#{keyFilter("[#{$key}]", "[&]", null)} {
#each $key, $value in $map {
--#{$key}: #{$value};
}
}
}
To "register" a new mode just nest another map in the $modes-map, you can add as many modes as you want. Keep in mind the "&"-mode represents the default-mode.
$modes: (
"&": (
//...
),
"dark": (
//...
),
//...
);
To register a new mode-depending variable just simply enter key and value to the respective mode.
$modes: (
"&": (
"color": #000,
"bgc": #fff,
"bgc-contrast": #eee,
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
To call a variable just use the v($key) function.
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
This compiling to:
body {
--color: #000;
--bgc: #fff;
--bgc-contrast: #eee;
}
body[dark] {
--color: #fff;
--bgc: #000;
--bgc-contrast: #424242;
}
body {
color: var(--color);
background-color: var(--bgc);
}
div.contrasted {
background-color: var(--bgc-contrast);
}
Note: you do not need to declare each variable for each mode. If a variable wasn't found for the current mode, this won't throw an error.For Example: This...
$modes: (
"&": (
//...
),
"dark": (
"color": #fff,
"bgc": #000,
"bgc-contrast": #424242,
//...
),
);
//...
body {
color: v(color);
background-color: v(bgc);
}
div.contrasted {
background-color: v(bgc-contrast);
}
... is totally fine.

How to make a mixin helper responsive using breakpoints?

I want to make this mixin responsive means it generate the media queries
based on breakpoints.
Here is my SCSS Code :
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
) !default;
#mixin overscroll-behavior-auto() {
-ms-scroll-chaining: chained;
overscroll-behavior: auto;
}
#mixin overscroll-behavior-contain() {
-ms-scroll-chaining: none;
overscroll-behavior: contain;
}
#mixin overscroll-behavior-none() {
-ms-scroll-chaining: none;
overscroll-behavior: none;
}
.overscroll-auto {
#include overscroll-behavior-auto();
}
.overscroll-contain {
#include overscroll-behavior-contain();
}
.overscroll-none {
#include overscroll-behavior-none();
}
#each $breakpoint in map-keys($grid-breakpoints) {
#include media-breakpoint-up($breakpoint) {}
}
I include my sass code thank you guys
Codepen
I made some modifications to the syntax of overscroll-behavior(). Mainly that its not overscroll-behavior-auto(), overscroll-behavior-contain() etc. any longer, but:
overscroll-behavior($behavior, $breakpoint);
This was necessary to not write the same code three times, since you can't generate mixins.
$overscrollBehavior: (
"auto": ["chained", "none" ],
"contain": ["none", "contain"],
"none": ["none", "none" ],
)!default;
$breakpoints: (
"xs": 0px,
"sm": 576px,
"md": 768px,
"lg": 992px,
"xl": 1200px,
"xxl": 1400px,
) !default;
#mixin overscroll-behavior($behavior, $breakpoint) {
$map: $overscrollBehavior;
#for $i from 1 through length($map) {
$label: nth(nth($map, $i), 1);
$values: nth(nth($map, $i), 2);
#if $behavior == $label {
$map: $breakpoints;
#for $i from 1 through length($map) {
$bp: nth(nth($map, $i), 1);
$bpValue: nth(nth($map, $i), 2);
#if $breakpoint == $bp {
#media (min-width: #{$bpValue}) {
-ms-scroll-chaining: unquote(nth($values, 1));
overscroll-behavior: unquote(nth($values, 2));
}
}
}
}
}
}
body {
#include overscroll-behavior(none, xs);
}
#media (min-width: 0px) {
body {
-ms-scroll-chaining: none;
overscroll-behavior: none;
}
}
If that's not quite what your looking for, let me know!

Reduce amount of CSS generated by a SASS function

I'm very new to the (I guess) more advanced features of SASS. Referencing this article I've been able to create a SASS setup that lets me easily do dark mode theming! I'd like to reduce the amount of CSS it generates, however, and wanted to know if I've reached the limit of my knowledge or just what SASS/CSS is capable of.
I have this map of "themes":
$themes: (
light: (
primaryOne: #f0f,
primaryTwo: #0ff
),
dark: (
primaryOne: #000,
primaryTwo: #111
)
);
I have these mixins/functions that were mostly lifted from the article:
#mixin themify($themes: $themes) {
#each $theme, $map in $themes {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#if $theme == dark {
#media (prefers-color-scheme: dark) {
#content;
}
} #else {
#content;
}
$theme-map: null !global;
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
And finally a custom mixin and the class where I'm using it:
#mixin set-color-primary {
#include themify($themes) {
color: themed('primaryOne');
}
}
.foo-container div {
#include set-color-primary;
}
Finally this generates CSS like the following:
.foo-container div {
color: #f0f;
}
#media (prefers-color-scheme: dark) {
.foo-container div {
color: #000;
}
}
I was hoping I could squeeze out a few more lines and get something more like:
.foo-container div {
color: #f0f;
#media (prefers-color-scheme: dark) {
color: #000;
}
}
Is this a limitation of my SASS knowledge or is this not possible in plain CSS?

Resources