LESS variables: Is this possible - less

So my code is having a major issue with types and I can't seem to solve it.
Whenever I subtract 1 from line 8 there are issues.
How can I resolve this?
#max-columns: 2;
#column-1-width-min: 30;
#column-2-width-min: 40;
.loop-column(#index) when (#index > 0) {
#max-minus-1a: "#{column-#{index}-width-min}";
#max-minus-1b: #max-minus-1a - 1; // problem child
#min: ~"min-width: #{column-#{index}-width-min}";
#max: ~"max-width: #{max-minus-1b}";
#media (#min) and (#max) {
[data-deckgrid="card"]::before {
content: "#{index} .column.card-column-#{index}";
}
}
.loop-column(#index - 1);
}
.loop-column(#max-columns);

In addition to the method you can find in this SO answer (as I have already mentioned in my comment above), I think the whole snippet can be simplified to something like (Less 1.5.x or higher):
#column-widths: 30, 40, 55, 500; // etc.
.loop-column(#index) when (#index > 0) {
.loop-column(#index - 1);
#min: extract(#column-widths, #index);
#max: (extract(#column-widths, #index + 1) - 1);
#media (min-width: #min) and (max-width: #max) {
[data-deckgrid="card"]::before {
content: "#{index} .column.card-column-#{index}";
}
}
}
.loop-column(length(#column-widths) - 1);
with the following CSS result:
#media (min-width: 30) and (max-width: 39) {
[data-deckgrid="card"]::before {
content: "1 .column.card-column-1";
}
}
#media (min-width: 40) and (max-width: 54) {
[data-deckgrid="card"]::before {
content: "2 .column.card-column-2";
}
}
#media (min-width: 55) and (max-width: 499) {
[data-deckgrid="card"]::before {
content: "3 .column.card-column-3";
}
}
I.e. you don't need to emulate arrays via "indexing variable names" since you can use arrays directly (Less array is just a comma or space separated value list, e.g. #padding: 1 2 3 4;).

Related

Less CSS responsive modifier

I'm trying to create a utility-first collection of CSS classes, similar to Tailwind but in Less. A big part of this is using responsive modifiers, using this className syntax: .large\:text-white.
The code below works great, except for one thing: the \: shouldn't be rendered on the default classes (the classes outside of the media queries. They should render using a period, as expected .foo.
I can't figure out how to solve this.
#screens: {
small: 320px;
medium: 768px;
large: 1024px;
}
#padding: {
0: 0;
10: 1rem;
20: 20rem;
30: 30rem;
}
#colors: {
white: #fff;
silver: hsla(0, 0%, 90%, 1);
}
#responsive-modifiers: true;
#config () {
.generate(pt, padding, #padding);
.generate(py, padding-top, #padding);
.generate(text, color, #colors);
.generate(background, background-color, #colors);
}
// Call the mixin
#config();
each(#screens, {
#media (min-width : #value) {
.#{key} when (#responsive-modifiers = true) {
#config();
}
}
})
.generate(#prefix, #property, #list) {
each(#list, {
&\:#{prefix}-#{key} {
#{property}: #value;
}
});
}
I solved it by adding a period/dot variable. Here's the final code. Works well:
#screens: {
small: 320px;
medium: 768px;
large: 1024px;
}
#padding: {
0: 0;
10: 1rem;
20: 20rem;
30: 30rem;
}
#colors: {
white: #fff;
silver: hsla(0, 0%, 90%, 1);
}
#responsive-modifiers: true;
#config () {
.generate(pt, padding, #padding);
.generate(py, padding-top, #padding);
.generate(text, color, #colors);
.generate(background, background-color, #colors);
}
#period: .;
#{period} {
#config();
}
each(#screens, {
#media (min-width : #value) {
.#{key}&\: when (#responsive-modifiers = true) {
#config();
}
}
})
.generate(#prefix, #property, #list) {
each(#list, {
&#{prefix}-#{key} {
#{property}: #value;
}
});
}

String interpolation for LESS Variables

I have a media query in LESS: #media (max-width: 1079px) { ... }. Basically I want to create a variable in LESS in which I can just say #media (*LESS variable goes here*) { ... } in my stylesheets. How can I interpolate the string max-width: 1079px into a LESS variable to make this happen?
Thanks
For the variable, you could escape the string value:
#media-max-width: ~"max-width: 1079px";
Code snippet:
#media-max-width: ~"max-width: 1079px";
#media (#media-max-width) {
p {
color: #f00;
}
}
Which will output the following:
#media (max-width: 1079px) {
p {
color: #f00;
}
}
You could also include the parenthesis in the variable too:
#media-max-width: ~"(max-width: 1079px)";
#media #media-max-width {
p {
color: #f00;
}
}

Less adds unexpected space to variable

For some reason, the output of nth child is rendered with un unexpected space. Can anyone help?
Renders:
// Part of render
body.domain-bsci-fta-local #block-domain-switcher ul li:nth-child( 3) {
background-color: #e14313;
}
From code:
// Variables
#a-primary: #018f9e;
#b-primary: #2b6a7c;
#c-primary: #e14313;
#d-primary: #009966;
#domain-a: 'a-local';
#domain-b: 'b-fta-local';
#domain-c: 'c-fta-local';
#domain-d: 'd-fta-local';
#domains: #domain-a #a-primary 1, #domain-b #b-primary 2, #domain-c #c-primary 3, #domain-d #d-primary 4;
// Call
body {
.generate-menus();
}
// Functions
.generate-menus() {
.for(#domains);
.-each(#domain) {
#dn: e(extract(#domain, 1));
#dc: extract(#domain, 2);
#dr: extract(#domain,3);
.generate-menu(#dn, #dc, #dr);
}
}
.generate-menu(#domainname, #domaincolor, #domaincount) {
&.domain-#{domainname} {
#block-domain-switcher {
ul {
li {
&:nth-child(#{domaincount}) {
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
}
.navigation .submenu {
background-color: #domaincolor;
}
}
}
// ............................................................
// .for
.for(#i, #n) {
.-each(#i)
}
.for(#n) when (isnumber(#n)) {
.for(1, #n)
}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
// ............................................................
// .for-each
.for(#array) when (default()) {
.for-impl_(length(#array))
}
.for-impl_(#i) when (#i > 1) {
.for-impl_((#i - 1))
}
.for-impl_(#i) when (#i > 0) {
.-each(extract(#array, #i))
}
Note: As mentioned by seven-phases-max in his comments to the question, this was a bug which has already been fixed in v2.x. Leaving this answer (with the work-around solution) as-is to help future readers who can't upgrade their compiler for whatever reason.
The problem happens only for selectors which use selector interpolation and are nested within one or more parent selectors. It can be solved by using a temporary variable which contains the pseudo-selector like below: (it uses escaped string feature)
Option 1:
ul {
li {
#selector: ~":nth-child(#{domaincount})"; /* the selector is formed here */
&#{selector} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
Option 2:
li {
#count: ~"(#{domaincount})";
&:nth-child#{count} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
Sample Compiled Output:
body.domain-a-local #block-domain-switcher ul li:nth-child(1) {
background-color: #018f9e;
}
Related Links:
concatenate values in less (css) without a space
Redudant space in interpolated selectors like nth(...)
As mentioned above and in the linked issue thread, the issue happens only when the selector is formed using selector interpolation and is nested under one or more parents.
This works
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body div:nth-child(#{num}) {
color: #444;
}
But this doesnt
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body {
div:nth-child(#{num}) {
color: #444;
}
}

set parent selector as a variable

div .mymixin('red','green')
.mymixin(#a, #b){
a {
color: #a;
span {
color: #b;
}
}
}
This code will produce the following css:
div a{color:red;}
div a span{color: green;}
I need it to produce this:
div a{color:red;}
div:not(.open) a span{color: green;}
I was trying to do something like this:
div .mymixin('red','green')
#parent: &;
.mymixin(#a, #b){
a {
color: #a;
#{parent}:not(.open) span {
color: #b;
}
}
}
But it doesn't work the right way, producing
div a &:not(.open) span{color:green;}
Is there a way to assign parent to a variable or do it some other way to achieve what I am after?
Thank you.
P.S. Here is the actual nesting I have:
.icfld(#name, #width, #height, #opacity, #open) {
> a {
...
> .icon {
...
}
&.disabled > .icon {
...
}
//&:not(.open) :not(.disabled):hover... will NOT work, because & at this point refers to
//"parent" > a and makes it "parent" > a:not(.open), while I need "parent":not(.open)
//the following line, however, works
&:not(.disabled):hover {
& when (#open=false) {
...
> .icon {
...
}
}
}
}
Came up with the following work around:
.mymixin2(#a, #b, #open){
a {
color: #a;
& when(#open = false){
span {
color: #b;
}
}
}
}
.mymixin1(#a,#b){
&.open {.mymixin2(#a,#b,true);}
&:not(.open) {.mymixin2(#a,#b,false);}
}
div .mymixin1('red', 'green');
This seems to do the trick.

Breakpoint Pair not functioning as expected

I've just installed Breakpoint via bower and I'm using a sample right from the wiki: https://github.com/Team-Sass/breakpoint/wiki/Basic-Media-Queries
$pair: 456px 794px;
#foo {
#include breakpoint($pair) {
content: 'Paired Media Query';
}
}
I should see this output:
#media (min-width: 456px) and (max-width: 794px) {
#foo {
content: 'Paired Media Query';
}
}
Instead, I see this:
#media (min-width: 456px), (min-width: 794px) {
#foo {
content: 'Paired Media Query';
}
}
Could anyone help me resolve this? Thanks
I needed to add this line to my gruntfile.js > compass > options >
require: '<%= yeoman.app %>/bower_components/compass-breakpoint/lib/breakpoint',