Less - how to generate values in a loop - css

I'm trying to generate a variable in the loop:
box-shadow: 835px 1456px #FFF, 1272px 796px #FFF, 574px 1357px #FFFetc ...
It is basic:
box-shadow: x1 y1 #fff, x2 y2 #fff, x3 y3 #fff, x4 y4 #fff ... xn yn #fff.
I tried to do it like this:
#maxPiont: 2000;
#minPoint: 0;
.init() {
.make-point(`Math.random()`, `Math.random()`, ""); // set initial value - ignore for now
}
.init();
.make-point(#newX, #newY, #old) {
.redefine() {
#X: floor(#newX * (#maxPiont - #minPoint + 1));
#Y: floor(#newY * (#maxPiont - #minPoint + 1));
#point: '#{X} #{Y} #fff';
#allPoints: "#{point}, #{old}";
}
}
.make-stars(#i) when (#i >0) { // sadly in loop we are in same scope all the time and trick wont work
.redefine();
.make-point(`Math.random()`, `Math.random()`, #allPoints);
.test
{
box-shadow: #allPoints;
}
.make-stars(#i - 1);
}
.make-stars(1); // <- scope 1
.make-stars(1); // <- scope 2
.make-stars(1); // <- scope 3
.make-stars(100); // <- scope 4
.make-stars(1); // <- scope 5
.make-stars(1); // <- scope 6
.make-stars(1); // <- scope 7
I know why it is not working in the loop and why it is working outside of it (scopes and less lazy loading ;/) How it can be done in a different way?
Did I think about making a variable per every step and add it? Is this possible or crazy idea?

You can use less lang merge function to aggregate shadow values from multiple properties into a comma separated list. No need to store all shadows into one variable.
Less:
#min: 1; // Min random number
#max: 100; // Max
// Generate single shadow with random position
.random-shadow() {
#x: `Math.floor(Math.random() * (#{max} - #{min}))`;
#y: `Math.floor(Math.random() * (#{max} - #{min}))`;
box-shadow+: unit(#x, px) unit(#y, px) #fff;
}
// Loop to set #count shadows to the element
.multiple-shadow(#count) when (#count > 0) {
.random-shadow();
.multiple-shadow(#count - 1);
}
a {
.multiple-shadow(5);
}
Css output:
a {
box-shadow: 79px 81px #fff, 76px 98px #fff, 67px 97px #fff, 44px 25px #fff, 54px 11px #fff;
}

Related

Using a SCSS variable (containing a calculation with parentheses) within a calc-function removes these parentheses on compilation

Problem
When I try avoid repetition of a part in a variable containing a CSS calc-function, I suddenly get different results on compilation due to missing parentheses.
Example
The following code works as intended (note that the horizontal padding only differs a bit at the end of the calc-function):
#output-element-1 {
padding:
0
calc((#{$line-height * 1em} + #{$padding-y * 2} + #{$border-height * 2} - #{$icon-size}) / 2 - 1px)
0
calc((#{$line-height * 1em} + #{$padding-y * 2} + #{$border-height * 2} - #{$icon-size}) / 2 + 1px);
}
Which compiles correctly into:
padding: 0 calc((1.5em + 3rem + 2px - 1.25rem) / 2 - 1px) 0 calc((1.5em + 3rem + 2px - 1.25rem) / 2 + 1px);
When I try to avoid duplication and isolate a bit of that calc-function into a separate variable, like this:
$output-element-padding-x: (#{$line-height * 1em} + #{$padding-y * 2} + #{$border-height * 2} - #{$icon-size}) / 2;
#output-element-2 {
padding:
0
calc(#{$output-element-padding-x} - 1px)
0
calc(#{$output-element-padding-x} + 1px);
}
This gets compiles into (note the missing parentheses):
#output-element-2 {
padding: 0 calc(1.5em + 3rem + 2px - 1.25rem/2 - 1px) 0 calc(1.5em + 3rem + 2px - 1.25rem/2 + 1px);
}
Example can also be viewed at: https://codepen.io/brenner/pen/YzXYxrj?editors=1100
Question
Is there perhaps a way you can force the parentheses to persist correctly through compilation?
Because of the way variables are read, I don't think you can do this. However, this is a really good use case for SASS #functions!
$line-height: 1.5;
$padding-y: 1.5rem;
$border-height: 1px;
$icon-size: 1.25rem;
#function output-element-padding-x($val: #{'1px'}, $operator: #{'+'}) {
#return calc( ( #{$line-height * 1em} + #{$padding-y * 2} + #{$border-height * 2} - #{$icon-size} ) / 2 #{$operator} #{$val} );
}
#output-element-2 {
padding:
0
output-element-padding-x( 1px, #{'-'} )
0
output-element-padding-x( 1px, #{'+'} )
}
I'm using the function to pass two parameters, the value you want to change per function and the operator - plus/minus/divide/multiply - whatever. I also set default values so you can just use mixin_test() and it will default to 1px and +
This compiles to:
#output-element-2 {
padding: 0 calc( ( 1.5em + 3rem + 2px - 1.25rem ) / 2 - 1px ) 0 calc( ( 1.5em + 3rem + 2px - 1.25rem ) / 2 + 1px );
}
This will keep your parenthesis as well as the grouping you have set up to make sure the order of operation is correct.

"Quoting" argument variable in LESS mixin

I have the following array of color definitions:
#colors: ~"black" #000, ~"white" #fff, ~"blue" #008FD6, ~"bluehover" #44A1E0, ~"grayborder" #DBDBDB;
And I use the following function to use those colors within CSS declarations.
.colorkey(#key) {
.-(length(#colors));
.-(#i) when (#i > 0) {.-((#i - 1))}
.-(#i) when (#key = extract(extract(#colors, #i), 1)) {
#colorkey: extract(extract(#colors, #i), 2);
}
.--() {#colorkey: #000} .--;
}
Usage:
.my-div {
.colorkey(~"black");
color: #colorkey
}
However I'd prefer to use the mixin like so:
.colorkey(black);
Without the quotes and tilde. Is it possible to modify the colorkey mixin to achieve this?
If your #colors can be defined without putting the color names in ~"...", you just need to make a minor change:
#colors: black #000, white #fff, blue #008FD6, bluehover #44A1E0, grayborder #DBDBDB;
.colorkey(#key:black) {
.-(length(#colors));
.-(#i) when (#key = extract(extract(#colors, #i), 1)) {
#colorkey: extract(extract(#colors, #i), 2);
}
.-(#i) when (#i > 0) {.-(#i - 1)}
}
.my-div {
.colorkey(bluehover);
color: #colorkey
}
Note that I
removed the extra set of parentheses in your .-(#i) when (#i > 0)
moved that recursive call to the end of .colorkey, and
dropped your .--() {#colorkey: #000} .--; and in its place used .colorkey(#key:black) {. (My guess is that was supposed to make .colorkey; color: #colorkey evaluate to color: #000 but actually it wasn't doing anything :) In the code you provided, to define that default you'd need instead to do .colorkey(#key: ~"black") { )

SASS - get previous items width and add variable onto it

I am try to access the previous item in a list using SASS so that I can add my variable $itemWidthIncrement onto the previous items value hence having an increment of 3px for each item:
$totalItems: 12;
$itemWidthIncrement: 3px;
$itemMarginIncrement: 1px;
#for $i from 1 through $totalItems {
.item-:nth-child(#{$i}) {
float: if($i % 2 == 0, left, right);
width:/*get previous item here*/ + $itemWidthIncrement;
}
}
Is this doable with SASS or am I not approaching this in the correct manner?
Try this - keep a $currentWidth variable and increment as necessary:
$totalItems: 12;
$itemWidthIncrement: 3px;
$itemMarginIncrement: 1px;
$currentWidth: 1px;
#for $i from 1 through $totalItems {
.item-:nth-child(#{$i}) {
float: if($i % 2 == 0, left, right);
width: $currentWidth;
}
$currentWidth: $currentWidth + $itemWidthIncrement;
}

Subtracting blur value from box-shadow property through SCSS

I have been cogitating on this for a while now. In my SCSS I have the following:
$shadow: 0 0 25px rgb(46, 46, 46);
div {
box-shadow: $shadow;
}
How can I subtract the 25px of blur by, let's say 10 so that I may use a blur of 15 pixels? Furthermore, how do I select the blur value? Considering I want to apply a box-shadow on a div element:
<div>Lorem Ipsum</div>
The $shadow variable is simply a list of values. If the blur is always going to be the 3rd item in the list (such as in your example), then what you're looking at is something like this:
div {
box-shadow: nth($shadow, 1) nth($shadow, 2) nth($shadow, 3) - 15px nth($shadow, 4);
}
If the blur is in another valid position in the list (maybe it's an inset shadow), then you'll need to start doing things like checking the length of the list and/or examine the first element to see if it is inset:
div {
#if nth($shadow, 1) == inset {
box-shadow: nth($shadow, 1) nth($shadow, 2) nth($shadow, 3) nth($shadow, 4) - 15px nth($shadow, 5);
} #else {
box-shadow: nth($shadow, 1) nth($shadow, 2) nth($shadow, 3) - 15px nth($shadow, 4);
}
}
Alternately, you could do it programatically:
$shadow: 0 0 25px rgb(46, 46, 46);
#function adjust-shadow($shadow, $position, $adjustment) {
$x: ();
$shift: if(nth($shadow, 1) == inset, -1, 0);
#for $i from 1 through length($shadow) {
$p: $i + $shift;
#if $position == color and $i == length($shadow) {
$x: append($x, $adjustment);
} #else if ($position == x-offset and $p == 1) or ($position == y-offset and $p == 2) or ($position == blur and $p == 3) {
$x: append($x, nth($shadow, $i) + $adjustment);
} #else {
$x: append($x, nth($shadow, $i));
}
}
#return $x;
}
.foo {
box-shadow: adjust-shadow($shadow, blur, -15px);
}
.bar {
box-shadow: adjust-shadow($shadow, color, blue);
}
.baz {
box-shadow: adjust-shadow(adjust-shadow($shadow, blur, -15px), y-offset, -2), adjust-shadow($shadow, blur, 10px);
}
Make a new variable for the blur value:
$blur: 25px - 10;
$shadow: 0 0 $blur rgb(46, 46, 46);
div {
box-shadow: $shadow;
}
Depending on your overall needs, you might be better served by writing a mixin.

How do you check for unitless variables with multiple values SASS

I try to create a flexible mixin where you can set the space for padding or margin from the same mixin
I based it on a bourbon for positioning
mixin setSpace($setSpace: padding, $setSpaceValues: 0 0 0 0){
#if type-of($setSpace) == list{
$setSpaceValues :$setSpace;
$setSpace: padding;
}
$top: nth($setSpaceValues, 1);
$right: nth($setSpaceValues, 2);
$bottom: nth($setSpaceValues, 3);
$left: nth($setSpaceValues, 4);
#if unitless($top and $right and $bottom and $left){
#{$setSpace}: $top+px $right+px $bottom+px $left+px ;
}
}
But I try to get the flexibility to be able to add individual units to it as well so that I can do also
.test{
#include setSpace(margin, 10% 0 5 5);
}
You could use the Sass if() function on each value to check for unitless ... maybe make define a function that does this - something in this direction perhaps:
#function setUnit($val){
#return if(unitless($val), $val * 1px, $val);
}
And then you can use it in your mixin:
#mixin setSpace($setSpace: padding, $setSpaceValues: 0 0 0 0){
#if type-of($setSpace) == list{
$setSpaceValues: $setSpace;
$setSpace: padding;
}
$top: nth($setSpaceValues, 1);
$right: nth($setSpaceValues, 2);
$bottom: nth($setSpaceValues, 3);
$left: nth($setSpaceValues, 4);
#{$setSpace}: setUnit($top) setUnit($right) setUnit($bottom) setUnit($left) ;
}
DEMO
in addition you could also just set the values in a loop ( - a bit more flexible and shorter):
#mixin setSpace($setSpace: padding, $setSpaceValues: 0 0 0 0){
#if type-of($setSpace) == list{
$setSpaceValues: $setSpace;
$setSpace: padding;
}
$out: ();
#each $val in $setSpaceValues{
$out: append($out, if(unitless($val), $val * 1px, $val));
}
#{$setSpace}: $out;
}
DEMO
Hope this gives you some ideas.
Your mixin just might be better suited as a function:
#function spacing($values: 0) {
$collector: ();
#each $v in $values {
$collector: append($collector, if(unitless($v) and $v != 0, $v * 1px, $v));
}
#return $collector;
}
.test{
margin: spacing(10% 0 5 5);
}
Output:
.test {
margin: 10% 0 5px 5px;
}
If all you're doing is transforming a single value, functions make it a little more clear that's all that's happening when you come back to read the code later.

Resources