Produce whitespace-free CSS string with SASS #while loop - css

I'm attempting to write a mixin that repeats a string as part of a selector. My desired output is very whitespace sensitive, but I can't find a way of making SASS compile without introducing said whitespace.
The desired output would be:
body:nth-child(n):nth-child(n){
background: blue;
}
body:nth-child(n){
background: green;
}
body {
background: red;
}
And I would like to achieve it using this invocation:
body {
#include increase-specificity( 2 ){
background: blue;
}
#include increase-specificity{
background: green;
}
background: red;
}
The problem is that the :nth-child(n) string can't accept any whitespace after the current selector (&), or between repetitions, without changing the functional CSS afterwards – and I can't find a way of writing a mixin that fulfils this without causing SASS errors.
This is how I've tried to express the mixin:
#mixin increase-specificity( $depth: 1 ){
&#while($depth > 0){#{'nth-child(n)'}$depth: $depth-1}{
#content;
}
}
And here's a Codepen to show it breaking.
My problems so far are:
SASS won't tolerate the #while loop immediately following & (&#while{…), but I can't think of any other way to avoid whitespace between the compiled strings.
Similarly, I can't find a way of writing a functonal #while loop without being liberal with whitespace: the current syntax falls over when it reaches the end of the the interpolator (#{…}) (don't know why), but removing interpolation causes it to trip up trying to interpret nth-child's parens.

There's so much going on wrong in your code (bad logic, missing whitespace, mismatched brackets, etc.), it's not worth the effort to try and explain why it doesn't work.
#mixin increase-specificity( $depth: 1 ) {
$sel: '';
#while($depth > 0) {
$sel: $sel + ':nth-child(n)';
$depth: $depth - 1;
}
&#{$sel} {
#content;
}
}
body {
#include increase-specificity( 2 ){
background: blue;
}
#include increase-specificity{
background: green;
}
background: red;
}

Related

sass mixins and css custom properties

aye folks!
i tried to build a sass mixin for css custom properties to make work a little bit easier.
my attempt does look like this:
#mixin mixin($value) {
background: unquote('$')#{$value};
background: var(--#{$value}, unquote('$')#{$value});
}
the output looks like this:
.example {
background: $value;
background: var(--colour, $value);
}
BUT sass doesn't convert my $value into the actual thing. the $value part end up in my final css file and ofc this doesn't work.
i tried to find a solution online but i'm either to dumb to find it or there isn't one. anyone here has an idea what i'm doing wrong?
I have try your code with this example:
#mixin mixin($value) {
background: unquote('$')#{$value};
background: var(--#{$value}, unquote('$')#{$value});
}
.example {
#include mixin(colour)
}
and it works.
The output is:
.example {
background: $colour;
background: var(--colour, $colour);
}
is what you want? Sorry but I don't have the authorization for add comment so I create this answer.
Edit: you can create a different mixin like this:
#mixin mixin($value, $color) {
background: $color;
background: var(--#{$value}, $color);
}
This help you to solve your problem?

Is there a way to assign the same value to 2 css properties at once?

Using CSS, LESS, or Sass can you assign the same value to 2 css properties at once?
Just like:
.c1, c2 {sameValue}
But instead like this:
.c2 { background-color:, color: sameValue}
You can't do this with CSS.
The easiest way to do this is use a variable. Here's how you'd do that in LESS
#color: red;
.demo {
background-color: #color;
color: #color;
}
and the same thing in Sass
$color: red;
.demo {
background-color: $color;
color: $color;
}
But you can also achieve the power you want. Here's one way you could do it in LESS:
.properties(#properties, #value, #i: 0) when (#i < length(#properties)) {
#property: extract(#properties, #i + 1); // get the property
#{property}: #value; // write the style
.properties(#properties, #value, (#i + 1)) // loop
}
.demo {
#props: background-color, color;
.properties(#props, red)
}
will compile to your desired
.demo {
background-color: red;
color: red;
}
How's it work?
.demo calls the .properties parametric LESS mixin, passing a list (sometimes called an array in other SO questions about CSS/etc) of properties (.properties's #properties parameter; in this example, #props) and the value to assign to all of them (.properties's #value parameter; in this example, red).
note that .properties has a counter parameter #i with a default value of 0.
.properties has a LESS CSS guard that checks to see if #i is less than the number of properties it was passed (held in #properties). It is! (#i is 0, and the properties' list is certain to be at least 1) Okay, so we're allowed past the guard.
Get the name of the property: use LESS's extract() on the first item in the list (we need to say #i + 1 because we started the #i counter at 0. we could have also started #i at 1, and guarded with when (#i < (length(#properties) + 1)) but that's harder to read)
At last: write the style. interpolate the variable holding the property name (#property) as a string (#{property}), and give it the value we originally passed to .properties in .demo (#value)
LESS loop! Run .properties again, but advance the counter #i one: .properties(staysTheSame, staysTheSame, (#i + 1))
.properties will run until it's looped through all the items in its #properties list. After that, #i will equal length(#properties), so we won't pass the when (#i < length(#properties)) guard.
Note that #props could be defined within .test, as above, or anywhere where .test will have access to it, and same for the value. You might end up with
#props: background-color, color;
#val: red;
#val2: green;
.properties {...}
.demo {
border-color: #val2;
.properties(#props, #val)
}
.demo2 {
.properties(#props, #val2)
}
If your browser supports Custom Properties (AKA CSS Variables) you can define a custom property for reuse:
.foo {
--example: red;
background-color: var(--example);
color: var(--example);
}
Otherwise you'll need to rely on a preprocessor such as LESS:
.foo {
#example: red;
background-color: #example;
color: #example;
}
or Sass:
.foo {
$example: red;
background-color: $example;
color: $example;
}
As far as being able to chain properties, I'm not aware of any preprocessor or specification that allows for that syntax.
I don't think you can in the way you described, you would need to use a variable. However you can achieve something similar with properties that share the same namespace.
.funky {
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#nested_properties
In Stylus is possible to do the Property lookup, which allows you to use properties in the current or closest parent ancestor and use it for calculations.
For you specific case you could write:
.demo
color: red
background-color: #color
which produces this CSS:
.demo {
color: red;
background-color: red;
}
It is a feature asked also for LESS but currently there are not plans to develop it. Currently in LESS (and in SASS) you must use one of already suggested solutions, passing by an additional variable.
With Stylus you can do this way using iteration and interpollation :
.modal
position : absolute
for bord in top bottom left right
{bord}: 1em
for larg in width min-width height min-height
{larg}: auto
for maxl in max-width max-height
{maxl}: none

LESS CSS: How to Create and Reference Associative Lists/Arrays [duplicate]

Is there some way to loop an array of name/value pairs LESS? Something like this:
arr = alice: black, bob: orange;
.for(arr) // something something //
.cl-#{name} {
background-color: #{value}
}
To generate something like this:
.cl-alice { background-color: black; }
.cl-bob { background-color: orange; }
I know that you can for-loop an array, but I'm unsure if it can be an array of objects rather than values in LESS.
The answer given by #seven-phases-max works very well. For completeness you should also notice that you can do the same in Less without the imported "for" snippet.
from lesscss.org
In trying to stay as close as possible to the declarative nature of
CSS, Less has opted to implement conditional execution via guarded
mixins instead of if/else statements, in the vein of #media query
feature specifications.
and
In Less a mixin can call itself. Such recursive mixins, when combined
with Guard Expressions and Pattern Matching, can be used to create
various iterative/loop structures.
So in Less you could write:
#array: alice black, bob orange;
.createcolorclasses(#iterator:1) when(#iterator <= length(#array)) {
#name: extract(extract(#array, #iterator),1);
.cl-#{name} {
background-color: extract(extract(#array, #iterator),2);
}
.createcolorclasses(#iterator + 1);
}
.createcolorclasses();
or indeed:
#array: alice black, bob orange;
.createcolorclasses(#iterator:1) when(#iterator <= length(#array)) {
#name: extract(extract(#array, #iterator),1);
&#{name} {
background-color: extract(extract(#array, #iterator),2);
}
.createcolorclasses(#iterator + 1);
}
.cl-{
.createcolorclasses();
}
In Less a "pair" (in its simplest form) can be represented as an array too, so it can be as simple as:
#import "for";
#array: alice black, bob orange;
.for(#array); .-each(#value) {
#name: extract(#value, 1);
#color: extract(#value, 2);
.cl-#{name} {
background-color: #color;
}
}
Note however that the ".for" thing is limited to the only loop per scope so it's better to rewrite above to something like this:
#import "for";
#array: alice black, bob orange;
.cl- {
.for(#array); .-each(#value) {
#name: extract(#value, 1);
&#{name} {
background-color: extract(#value, 2);
}
}
}
The imported "for" snippet (it's just a wrapper mixin for recursive Less loops) can be found here (with examples here and here).
While it is useful from the other answers to know that Less supports recursive functions and mixins, there is now a much simpler answer to this simple question. This solution is tested to work with Less v3.9, but should work back to Less v3.7 when each was introduced.
.array() {
alice: black;
bob: orange;
}
each(.array(), {
.cl-#{key} {
background-color: #value;
}
});
The output is tidy:
.cl-alice {
background-color: black;
}
.cl-bob {
background-color: orange;
}
Want more? Well, as they say, "You can haz more". Use #index to use the 1-based index in the formula above.
Here is one "parametric mixins" which you can use with "key:value" pairs.
Array of "key:value" pairs is defined as follows: #array: "key:value", "key:value";
// imported "for" snippet (it's just a wrapper mixin for recursive Less loops)
// http://is.gd/T8YTOR
#import "for";
// loop all items and generate CSS
.generate_all(#array) {
.for(#array);
.-each(#item) {
#name: e(replace(#item, ':(.*)', ''));
#value: replace(#item, '^[^:]*:', '');
#{name} {
z-index: e(#value);
}
}
}
Definition:
#array_test: ".test:9000", "header .mainNav:9000", "footer:8000", "li.myclass:5000";
Test
.generate_all(#array);
Result:
.test {
z-index: 9000;
}
header .mainNav {
z-index: 8000;
}
footer {
z-index: 7000;
}
li.myclass {
z-index: 5000;
}
It works for me using grunt + less#1.7.4

Dynamic :last-child in SASS

I've been messing around with SASS for a while and have made some nice functions that makes use of #if #else if etc. However I have the following two pieces of code on a Mixin:
&:last-child { float: right; margin-right: 0; }
&:only-child { float: left; margin-right: 0; }
This obviously applies both of these pieces of code to every element I include this particular Mixin on. Is there a way of dynamically checking whether an element is a :last-child / :only:child with an #if statement or any other methods?
Something like:
#if :last-child == True { float: right; margin-right:0; }
Hopefully I've explained that well enough.
Thanks
Sass doesn't know anything about your document. It can't check if the element is the first-child or only-child. Sass can only be used to generate valid CSS.
If you're looking for the last-child that is not an only-child, this is your selector:
&:last-child:not(:only-child) {
// styles
}
http://tinker.io/1717b
If you're wanting to disable output of one or the other (because you already know that only one of them will be applicable in this instance), then you'll need an extra argument passed to your mixin:
#mixin foo($children: true) {
#if $children or $children == last-child {
&:last-child {
// styles
}
}
#if $children or $children == only-child {
&:only-child {
// styles
}
}
}

LESS CSS: Reuse generated .#{name} class as a mixin

I'm using LESS CSS 1.3.3. Sorry if this question has already been asked, I didn't find anything relevant on the web.
I have several class generators that look like this (example extremely simplified, just enough to trigger the error):
#genMarginTop (#name, #size) {
.#{name} { margin-top: #size; }
}
Then I use them to generate some actual classes:
#genMarginTop(mtStandard, 40px);
#genMarginTop(mtHalf, 20px);
So far, so good, LESS correctly generates those classes and I can use them in the HTML.
However when I want to reuse such a generated class as a mixin somewhere else, I get an error:
.someClass {
.mtStandard; // won't work, see error below
// more stuff
}
The error I get is:
NameError: .mtStandard is undefined in /.../example.less:161:4
160 .someClass {
161 .mtStandard;
162 // more stuff
Of course I try to use the mixin after the class has been generated. It looks like LESS somehow won't register such generated classes internally after it generates them, but I could well be wrong.
Is there a way to reuse such generated classes as mixins in other classes? Being quite new with LESS, and their documentation being rather sparse about generated classes, I'm at a total loss (especially since this is the only syntax that seems to be accepted for mixins).
Thanks for reading me.
Note: The reason why I use such class generators is because they are much more complex than the example above (think nested classes that all depend on a common set of parameters), and I'm embedding the generated classes in various #media queries to support any device type in a "Zen" fashion. In the end I get something like:
#media (max-width: 1024px) {
#genSomething(something, somethingParam1, ...);
#genSomething(somethingElse, somethingElseParam1, ...);
#genStuff(stuff, stuffParam1, ...);
}
#media (max-width: 240px) {
#genSomething(something, somethingParam2, ...);
#genSomething(somethingElse, somethingElseParam2, ...);
#genStuff(stuff, stuffParam2, ...);
}
// etc
Solution / test case
Here's a test case for #MartinTurjak 's solution, I can confirm that this works as expected, nested classes and everything:
.explicit {
margin-top: 1;
input { margin-top: 1; }
}
.reuseExplicit {
.explicit;
margin-bottom: 1;
}
#generator (#arg) {
margin-top: #arg;
input {
margin-top: #arg;
}
}
.generated { #generator(1); }
.reuseGenerated {
.generated;
margin-bottom: 1;
}
Which correctly generates: (notice how explicit/generated yield the very same result)
.explicit {
margin-top: 1;
}
.explicit input {
margin-top: 1;
}
.reuseExplicit {
margin-top: 1;
margin-bottom: 1;
}
.reuseExplicit input {
margin-top: 1;
}
.generated {
margin-top: 1;
}
.generated input {
margin-top: 1;
}
.reuseGenerated {
margin-top: 1;
margin-bottom: 1;
}
.reuseGenerated input {
margin-top: 1;
}
Unfortunately. The selector interpolation is just string interpolation, and the string gets then printed into css, so no class object is generated in the less run.
So you can design a generator/mixin, that includes your operation:
#genMarginTop (#size) {
margin-top: #size;
}
But then build classes by calling the mixins / generators:
.mtStandard {#genMarginTop(40px);}
.mtHalf {#genMarginTop(20px);}
And this way they are class objects that you can use for mixin =)
.someClass {
background-color: #FFF;
.mtStandard;
//more of this stuff
}
This looks a bit silly in this simple example, but maybe something like this:
#bggenerator (#color) {
background-color: #color;
}
#bggenerator (#color, dark) {
#blend : #color + #842210;
background-color: darken(#blend, 30%);
}
#bggenerator (#color, #url, #rest) {
background: "#{color} url('#{url}') #{rest}";
}
.mtStandard {
#genMarginTop(40px);
}
.someClass {
.mtStandard;
#bggenerator(#FFF, "bgimage.png", left top no-repeat);
//more of this stuff
}
Or something that does even more exciting stuff with the arguments
UPDATE LESS 1.7+ (Works as Desired)
The .#{name} syntax will now work just as the original question had desired.
LESS 1.4+ Workaround to Actually Use Dynamic Class Names
I came up with a work around for this while working on another question, so I'm posting it as a second answer, since it goes in a totally different direction than my earlier answer.
This solution requires a couple of steps (so is not as convenient as a final fix in LESS would be), but would give actual functionality of being able to use dynamically generated class names.
First: Define your dynamic classes
This is just as you planned.
#genMarginTop (#name, #size) {
.#{name} { margin-top: #size; }
}
#genMarginTop(mtStandard, 40px);
#genMarginTop(mtHalf, 20px);
Second: Compile that file into CSS
So lets say you compile your dynamicClasses.less into dynamicClasses.css. This causes the dynamic class names to "resolve" to actual classes.
Third: Import that CSS as LESS into a 2nd LESS file that uses the dynamic class names
Using type casting for #import, we do this:
#import (less) dynamicClasses.css;
This takes those resolved class names in the dynamicClasses.css file and imports them as LESS, which makes all the class names now available as mixins. So you can do as you desired:
.someClass {
.mtStandard; // will work
// more stuff
}
I agree. It looks like LESS does not register those classes for mixin purposes.
Incomplete Solution
This LESS code:
#genMarginTop (#name, #size) {
#genMarginTopNameCheck: #name;
.get(#name) when (#name = #genMarginTopNameCheck) { margin-top: #size; }
.#{name} { .get(#name); }
}
#genMarginBot (#name, #size) {
#genMarginBotNameCheck: #name;
.get(#name) when (#name = #genMarginBotNameCheck) { margin-bottom: #size; }
.#{name} { .get(#name); }
}
#genMarginTop(mtStandard, 40px);
#genMarginBot(mbStandard, 20px);
#genMarginTop(mtSpecial, 80px);
.myClass {
.get(mtStandard);
.get(mbStandard);
}
.myClass2 {
.get(mtSpecial);
.get(mbStandard);
}
Generates this CSS
.mtStandard {
margin-top: 40px;
}
.mbStandard {
margin-bottom: 20px;
}
.mtSpecial {
margin-top: 80px;
}
.myClass {
/* NOTE the mtStandard definition is missing here !!! */
margin-bottom: 20px;
}
.myClass2 {
margin-top: 80px;
margin-bottom: 20px;
}
Explanation and Disscussion of Final Issue to Resolve
Each mixin is defining a guarded .get() mixin based off the #name to get the styles, and that is cross checked to a unique NameCheck variable name for that mixin. All your actual code is defined in the .get(), and that mixin is used to actually generate the .#{name} class code.
This works fine every time for generating the actual class name. However, the getter function at present is only working for the class name last defined by a use of the mixin. So as you can see above, my get call for mtStandard is not working because my setting of mtSpecial has apparently overwritten the #genMarginTop .get() mixin with the mtSpecial definition.
Now I assume you are going to want to call #getMarginTop and your other such mixins more than once, so obviously this is still an incomplete solution. I've figured out how you can get the class generated by the top level mixin to be used as a 'mixin' for another class using the .get(), but I haven't figure out how to make the .get() not get overridden when the top level mixin is called again.

Resources