SASS create multiple variables at once - html

Is it possible to create a single SASS variable using map that then creates a variable for each key-value pair?
For example, I want this:
$heading: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem );
to turn into this:
$heading-1: 5rem;
$heading-2: 4.5rem;
$heading-3: 4rem;
$heading-4: 3.5rem;
$heading-5: 3rem;
$heading-6: 2.5rem;
I used the map to create class names for each variation, but I want to create the variables themselves and not the class names.

Maps are used to give you a more hierarchical way of visualising and referencing groups of data/values. You can use values from maps using map-get:
/* Map */
$heading: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem
);
/* Use map-get to make use of values from a map */
.heading-5 {
font-size: map-get($heading, 5);
}
/* Output */
.heading-5 {
font-size: 3rem;
}
You could have a simple function to use these, too:
/* Map */
$heading: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem
);
/* Simple reusable function to reference heading sizes */
#function heading($heading-size) {
#return map-get($heading, $heading-size);
}
.heading-4 {
font-size: heading(4);
}
/* Output */
.heading-4 {
font-size: 3.5rem;
}
Otherwise, if you want individual variables then you should just make individual variables.
Related: You can use an #each loop to produce CSS custom properties (rather than sass variables), which may actually suit your use case:
/* Map */
$heading: (
1: 5rem,
2: 4.5rem,
3: 4rem,
4: 3.5rem,
5: 3rem,
6: 2.5rem
);
/* Use an #each loop to generate CSS custom properties for all the map entries */
:root {
#each $heading-level, $heading-size in $heading {
--heading-#{$heading-level}: #{$heading-size};
}
}
/* Output */
:root {
--heading-1: 5rem;
--heading-2: 4.5rem;
--heading-3: 4rem;
--heading-4: 3.5rem;
--heading-5: 3rem;
--heading-6: 2.5rem;
}

Related

How to generate sizes for all headers using SASS with modular scale

I'm looking to create sizes for H1 through H6 using SCSS. This example assumes I've created the variables $base-size and $modular-scale. Specifically:
h6: $base-size * $modular-scale * 1
h5: $base-size * $modular-scale * 2
h4: $base-size * $modular-scale * 3
...
h1: $base-size * $modular-scale * 6
I can't figure out how to generate each of these classes using a mixin or a function (or whatever appropriate feature for this task).
so far I have:
#mixin make-header-size ($type, $scale) {
.#{$type} {
$type * $scale * (...scalar)
}
}
Not sure how to complete the rest so that I can succinctly generate each of these sizes.
Here you have a simple and small #for loop to generate the six heading styles with a scale variable that indicates the amount of px the headings grow from h6 to h1:
// h6 starts at $base-font-size
// headings grow from h6 to h1 by $heading-scale
$base-font-size: 18px;
$heading-scale: 8; // amount of px headings grow from h6 to h1
#for $i from 1 through 6 {
h#{$i} {
font-size: $base-font-size + $heading-scale * (6 - $i);
}
}
And a demo codepen to play with it.
Just a small function that I have created
$types : h6 h5 h4 h3 h2 h1;
$base-size : 16px;
$modular-scale : 0.5;
#each $type in $types{
#{$type} {
font-size : $base-size * $modular-scale;
$modular-scale : $modular-scale + 1;
}
}
See the PEN
I'm using this function for my personal use.
$headings : h1 h2 h3 h4 h5 h6;
$font-size-base : 16px;
$font-size-upper : 36px;
$font-size-dec : 4px;
#each $heading in $headings{
#{$heading} {
font-size : $font-size-upper;
font-size : ($font-size-upper / $font-size-base) + em;
}
$font-size-upper : $font-size-upper - $font-size-dec;
}
Check it out.
#Fabian's and #Jinu's answers are pretty good. However, I wanted my scss sizes to be context and screen-size dependent. Here's my mobile-first solution, which I arrived at by merging #Fabian, this blog post, and a media-query mixin. Note while you look over the code, that if your context is not in context-font, no font-size will be defined, so be wary of that.
// https://codepen.io/zgreen/post/contextual-heading-sizes-with-sass
// https://stackoverflow.com/questions/38426884/how-to-generate-sizes-for-all-headers-using-sass-with-modular-scale
//define vars and mix-ins
$context-font: ( header: 1, section: 1, article: 0.67, footer: 0.5, aside: 0.33 ); // Make fonts change size dependent upon what context they are in
$step-size-font: 0.333333; //step size for incremental font sizes
$amplifier-font-mobile: 1.2; //convenience var to in/decrease the values of header values in one place
$amplifier-font-desktop: 1.4; //convenience var to in/decrease the values of header values in one place
$on-laptop: 800px;
$margin-heading: 0.67em 0;
#mixin media-query($device) {
//scss mobile-first media query
#media screen and (min-width: $device) {
#content;
}
}
//function to generate the actual header size
#mixin heading-size($size) {
$context-size-computed: $size * $step-size-font;
font-size: $context-size-computed * $amplifier-font-mobile + em;
#include media-query($on-laptop) {
font-size: $context-size-computed * $amplifier-font-desktop + em;
}
}
// Apply the stylings using the vars and mix-ins
// context-free header styling
#for $i from 1 through 6 {
h#{$i} {
font-weight: bold;
margin: $margin-heading;
}
}
//iterate thru all the contexts in context-font, setting the header sizes in each
#each $element, $value in $context-font {
#{$element} {
//set header sizes 1-6
#for $i from 1 through 6 {
h#{$i} {
$bigger-size-proportional-to-smaller-number: (6-$i);
#include heading-size($bigger-size-proportional-to-smaller-number * $value);
}
}
}
}
Edit: Another (simpler/better?) way of getting context-specific font sizes is to use em font sizes, and change the font-size in your contexts. You would only do this once per context (article, section, div, whatever), and it would affect all text inside of it. That's because em is a relative font size.

Random background image for each class instance

I have a sass function which returns a random url from a given set of urls as follows:
#function randomUrl(){
$images: (
"/images/watermarks/area-watermark.png",
"/images/watermarks/bar-watermark.png",
"/images/watermarks/line-watermark.png",
$img: nth($images, random(length($images)));
#return $img;
}
and i am assigning it to a class as follows:
.myClass{
background-image: url(randomUrl());
}
What i want now is to get a random image FOR EACH class instance, i.e, if i have 10 divs with class "myClass" in my HTML, i want the background images of each div to be different. My approach till now just gives me one random image which appears in all the divs everytime i compile.
The random() function does exactly what it sounds like: it generates a random number between 2 specified numbers. There is no guarantee that the numbers will be different each time the function is called because that's not how random works.
What you need is a way to shuffle your list, but there is no such function to do that in the Sass standard library. There are a couple of 3rd party libraries that do:
https://github.com/at-import/SassyLists (sl-shuffle)
https://github.com/mknadler/randomize.scss (shuffle)
The implementation in both libraries is nearly identical (this one was lifted from randomize.scss):
#function shuffle($list) {
$list-length: length($list);
#while($list-length > 0) {
$rand: random($list-length);
$temp: nth($list, $rand);
$list: set-nth($list, $rand, nth($list, $list-length));
$list: set-nth($list, $list-length, $temp);
$list-length: $list-length - 1;
}
#return $list;
}
If you're intentionally avoiding iterating over a list, you could use it like this:
#import "SassyLists";
$last-pos: 0;
$images: sl-shuffle(
"/images/watermarks/area-watermark.png"
"/images/watermarks/bar-watermark.png"
"/images/watermarks/line-watermark.png");
#function randomUrl(){
$last-pos: if($last-pos == length($images), 1, $last-pos + 1) !global;
#return nth($images, $last-pos);
}
.myClass {
background-image: url(randomUrl());
}
.myClass {
background-image: url(randomUrl());
}
.myClass {
background-image: url(randomUrl());
}
Output:
.myClass {
background-image: url("/images/watermarks/line-watermark.png");
}
.myClass {
background-image: url("/images/watermarks/area-watermark.png");
}
.myClass {
background-image: url("/images/watermarks/bar-watermark.png");
}
Though I recommend just using iteration instead and cut out the use of the function all together:
#import "SassyLists";
$images: sl-shuffle(
"/images/watermarks/area-watermark.png"
"/images/watermarks/bar-watermark.png"
"/images/watermarks/line-watermark.png");
#for $i from 1 through length($images) {
.myClass-#{$i} {
background-image: url(nth($images, $i));
}
}
http://sassmeister.com/gist/d0c65d02be52aa31f836

Sass lighten function issue

Well, I have a problem with Sass function lighten(), this is my code:
#for $i from 1 through 4{
.par-#{$i}{
background: lighten(#08725D, #{$i}0%);
padding: 10px;
border-radius: 5px;
}
}
I think it's pretty obvious what I want to do, I just want to go through that variable and increment the lightening of the bg color (while I increment the number in the class), but I have this error in console:
error main.scss (Line 111: $amount: "10%" is not a number for `lighten')
I need help please.
$i contains a number. Keep using it as a number instead of using concatenation :
#for $i from 1 through 4{
.par-#{$i}{
background: lighten(#08725D, $i * 10);
padding: 10px;
border-radius: 5px;
}
}

Why won't this LESS css sizing mixin compile?

I'm trying to create a mixin that'll take two parameters and output sizing in px and rem. This is the code:
.sizing (#cssProperty; #sizeValue) {
#cssProperty: ((#sizeValue * #basefont) * 1px);
#cssProperty: (#sizeValue * 1rem);
}
Usage would be like:
h2 {
.sizing(font-size; 1)
}
Which should output (depending on what basefont size is defined):
h2 {
font-size: 12px;
font-size: 1rem;
}
But simpLESS won't compile it, and says there's an error in these two lines:
.sizing (#cssProperty; #sizeValue) {
.sizing(font-size; 1);
What am I doing wrong? Is it because of the variable property names?
Just noticed that you are trying to use variables as property names instead values which is not supported by less.
There is a hack highlighted in this answer as workaround:
How to pass a property name as an argument to a mixin in less
.mixin(#prop, #value) {
Ignore: ~"a;#{prop}:#{value}";
}
LESS does not allow to use a variable as a CSS property name.
In your code above #cssProperty: ((#sizeValue * #basefont) * 1px); is actually a definition of the new #cssProperty variable and not a CSS property: value statement, hence it produces no CSS output.
There's a workaround for what you want to achieve though, see 14868042, 18558368 etc...

Can I use a variable for a function name?

I'm trying to loop through a list which automates several functions. Unfortunately the function is not evaluated.
For example:
$colors:
red,
blue,
green;
#each $color in $colors{
.color-#{$color} {
value: $color(#F15258);
}
}
(I've simplified my example code to make it easier to illustrate).
Unfortunately this just outputs the value of $key and the color #F15258.
ie:
value: red #F15258;
Can I get SASS to pass in the variable as the function name so it actually evaluates `red(#F15258)?
It should output:
value: 241;
Any thoughts?
As of Sass 3.3 you can do this using the call() function:
$colors:
'red',
'blue',
'green';
#each $color in $colors{
.color-#{$color} {
value: call($color, #F15258);
}
}
Output:
.color-red {
value: 241;
}
.color-blue {
value: 88;
}
.color-green {
value: 82;
}
Note that your variables must be a string: red is a Color while 'red' is a String.
SASS does not allow dynamic names, and that's a good thing.
To use a dynamic name, you'll have to use a template to generate your SASS prior to compiling it. See how Compass does it: https://stackoverflow.com/a/16129685/901944
This increases the complexity of your project greatly and i strongly advise against.
Instead, use a function that accepts the name as a parameter:
#function parse-color($color) {
// Do whatever you want here
}
.color-red {
color: parse-color(red);
}
Note that instead of hardcoding the second color you can have it as an argument with a default value:
#function parse-color($first-color,
$second-color: #F15258) {
// Do whatever you want here
// For example:
#return mix($first-color, $second-color);
}
$colors:
red,
blue,
green;
#each $color in $colors{
.color-#{$color} {
color: parse-color($color);
}
}
See a demo: http://sassbin.com/gist/6193779/
The variable with multiple values are called as lists in sass.
so,
you can make a list like:-
$colors: red blue green; //list of colors
#each $color in $colors{
.color-#{$color} {
color: ($color);
}
}