How to throw an error in the LESS compiler - css

Question
Is there any way to (programmatically) throw an error in the LESS compiler?
Why?
I have been fiddling around with mixin guards today, because I wanted to generate my CSS margin based upon element size and element count.
I thought it would be cool to directly throw an error on compilation, when the elements won't fit in the wrapper.
Info: I am using the lessc compiler to compile my LESS code to CSS. I am not using any Javascript library to compile it on execution time.
LESS source
// Variables
#wrapper-small: 830px;
#wrapper-big: 1200px;
.col-fixed(#size, #count, #wrapper) when ((#size*#count) <= #wrapper)
{
width: unit(#size, px);
margin-right: unit( (#wrapper - #count * #size) / (#count - 1), px);
}
.test_col_fixed {
// will fail the mixin guard and output no generated CSS
.col-fixed(340, 3, #wrapper-small);
// would work if not in comment
// .col-fixed(340, 3, #wrapper-big);
}
Generated CSS (small wrapper)
No output, because the code will not be generated due to the not matching mixin guard when ((#size*#count) <= #wrapper) // 3*340 <= 830 is false.
Generated CSS (with working solution, big wrapper)
.test_col_fixed {
width: 340px;
margin-right: 90px;
}

Suggested, but strictly not recommended solution by Harry
.col-fixed(#size, #count, #wrapper) {
& when ((#size*#count) <= #wrapper) {
width: unit(#size, px);
margin-right: unit( (#wrapper - #count * #size) / (#count - 1), px);
}
& when ((#size*#count) > #wrapper) {
/* there is no such variable and hence when the input value is not valid,
compiler will complain that variable is undefined */
output: #bwahahaha;
}
}

Related

How to get division and math function to work on variable map item(s) in less

I'm relatively new to less and I have this less code which I want to compile down to css (the .song block specifically). I'm learning through a course.
Less code:
#breakpoints: {
desktop: 989px;
tablet: 767px;
phone: 480px;
};
#media only screen and (min-width: #breakpoints[desktop]) {
#minWidth: #breakpoints[desktop];
.container {
width: #minWidth;
}
/*Division*/
.song {
width: #breakpoints[desktop] / 3;
}
/*With ceil function*/
.song {
width: ceil(#breakpoints[desktop] / 3);
}
}
When I divide the number, it doesn't give an error but doesn't work as intended.
Instructors css output:
.song {
width: 329.66666667px;
}
My own css output:
.song {
width: 989px / 3;
}
When I'm using a math function like ceil, it gives an error:
Instructors css output:
.song {
width: 330px;
}
My own result (error):
ArgumentError: Error evaluating function ceil: argument must be a number
I have knowledge of javascript so I get what the error means. What I don't understand is how, for the same code I wrote above, the instructor's code compiled fine but mine doesn't. I tried browsing but I haven't gotten any answer that addresses this issue. The instructor used lessc version 3.8.1, I'm using lessc version 4.1.0. Any help will be really appreciated. Thanks.
I've been able to get the calculation to work by inserting the expressions in parenthesis like so:
Division:
.song {
width: (#breakpoints[desktop] / 3);
}
Ceil function:
.song {
width: ceil((#breakpoints[desktop] / 3));
}
Got answer at github issue/thread from this particular comment.

How to Assign CSS Variable Value to scss Variable or Expression

I'm trying to build my own tiny scalable grid in CSS / scss.
So far I found this decision:
:root {
--page-width: 1170px;
--gutter: 15px;
--columns: 12;
}
.wrapper {
max-width: var(--page-width);
margin-right: auto;
margin-left: auto;
padding-left: var(--gutter);
padding-right: var(--gutter);
}
.row {
margin-left: calc(-1 * var(--gutter));
margin-right: calc(-1 * var(--gutter));
}
.col {
display: block;
margin-left: var(--gutter);
margin-right: var(--gutter);
}
Then I tried to use scss to shorten columns classes description (which at the same time will allow me to change number of columns in one single place in whole code - in CSS Variable --columns) like this
#for $n from 1 through var(--columns) {
.col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); }
}
but it didn't work. The interesting detail is that when I change #for statement from #for $n from 1 throughvar(--columns)`` to #for $n from 1 through12 it compiles well. And there is no problem in compiling CSS-Variable inside #for body. .col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); } compiles well into needed series of classes.
If I use scss variable $columns instead of CSS variable then I'll need to import my grid.scss file into all other scss files of the project.
It's my first question on StackOverflow, so let me know if any other details are needed.
CSS and SCSS variables are two very different things (please see this pen)
To make it work you need a static variable for SCSS to compile
// static (SCSS) variables used produce CSS output
$page-width: 1170px;
$gutter : 15px
$columns: 12;
// dynamic (CSS) variables used at run-time
// note the values are interpolated
:root {
--page-width: #{$page-width};
--gutter : #{$gutter};
--columns: #{$columns};
}
// the for loop is aimed at producing CSS output
// ... why you need the static variable
#for $n from 1 through $columns {
// the content becomes CSS output
// ... why you can use dynamic variables
.col-#{$n} {width: calc( #{$n} / var(--columns) - var(--gutter) * 2 ); }
}
You need to use interpolation (eg. #{$var}) on your variable in order for Sass to treat it as a CSS property. Without it, you're just performing variable assignment.
#mixin w_fluid($property_name, $w_element, $w_parent:16) {
#{$property_name}: percentage(($w_element / $w_parent));
}
The accepted answer is no longer valid. Newer versions of SASS require interpolation to be used for variables.
Refer here for more details
$accent-color: #fbbc04;
:root {
// WRONG, will not work in recent Sass versions.
--accent-color-wrong: $accent-color;
// RIGHT, will work in all Sass versions.
--accent-color-right: #{$accent-color};
}

Subtract from a value obtained through variable interpolation

I'm having trouble calculating a value when using variable interpolation.
Here's an example on the Less preview site: http://goo.gl/GVHXUs
Below is my code:
#breakpoint-sm: 600px;
#breakpoint-md: 800px;
.Mq(#breakpoint; #rules; #maxMin: min) {
& when (#maxMin = min) {
#query: ~"(min-width: #{breakpoint-#{breakpoint}})";
#media screen and #query {#rules();};
}
& when not (#maxMin = min) {
#break: ~"#{breakpoint-#{breakpoint}}" - 1;
#query: ~"(max-width: #{break})";
#media screen and #query {#rules();};
}
}
.test {
.Mq(sm; {
width: 100%;
height: 200px
}; max);
.Mq(md; {
width: 100%;
height: 200px
});
}
Result:
#media screen and (max-width: 600px - 1) {
.test {
width: 100%;
height: 200px;
}
}
#media screen and (min-width: 800px) {
.test {
width: 100%;
height: 200px;
}
}
So what I'm trying to achieve is that when something other than min is passed to the #maxMin it should subtract 1 from the breakpoint. I guess I'll be the laughing stock of Stackoverflow now, but hell, I can't figure it out.
The output of ~"#{breakpoint-#{breakpoint}}" is always a string and so the compiler just appends the number to the string instead of performing the math operation.
One way would be to use a temporary variable like shown below (have added only the part that needs modification) and then perform the arithmetic operation.
.Mq(#breakpoint; #rules; #maxMin: min) {
/* the rest of the mixin */
& when not (#maxMin = min) {
#temp: ~"breakpoint-#{breakpoint}";
#break: (##temp - 1); /* the braces are mandatory, without which it again appends */
#query: ~"(max-width: #{break})";
#media screen and #query {#rules();};
}
}
/* the selector blocks and mixin calls */
Below are few things that I found while working on the solution which have left me stumped. I'm trying to find the reason and will update the answer when I do find it out.
The braces play an important role in the #break variable. Without it, the output in media query is still a concatenation. However, if the same variable is used outside the media query (in a normal property-value pair like prop: #break, it prints the subtracted value).
The below code returns concatenated value (800px - 1)
#break: ~"#{breakpoint-#{breakpoint}}" - 1;
prop: #break;
whereas the below gives a "Operation on invalid type" compiler error.
#break: ~"#{breakpoint-#{breakpoint}}";
prop: #break - 1;
while I can see the reason behind them (first one results in string concatenation whereas second says subtraction can't happen on a string value), I am a bit stumped as to why the behavior is not consistent between the two.
(You are definitely not a laughing stock. Though I knew the reason for the problem, it took time for me to find a solution.)

Using nested columns in Semantic.gs with a FIXED layout

I have implemented a fluid layout with Semantic.gs and some nested columns with LESS. But now our client decided they want the layout to be fixed.
I thought I could simply comment out the line #total-width:100% in grid.less, however now the other LESS files give an error on lines with the .row() mixin.
Is there a workaround for this?
Here is the relevant portion of grid.less
/////////////////
// Semantic.gs // for LESS: http://lesscss.org/
/////////////////
// Defaults which you can freely override
#column-width: 20;
#gutter-width: 10;
#columns: 47;
// Utility variable - you should never need to modify this
#gridsystem-width: (#column-width*#columns) + (#gutter-width*#columns) * 1px;
// Set #total-width to 100% for a fluid layout
//#total-width: #gridsystem-width;
//#total-width: 100%;
// Uncomment these two lines and the star-hack width/margin lines below to enable sub-pixel fix for IE6 & 7. See http://tylertate.com/blog/2012/01/05/subpixel-rounding.html
#min-width: 980;
#correction: 1 / #min-width * 100 * 1.5%;
Here is the problematic section of the LESS file. The LESS compiler gives the error 'Compiler Errors
variable #total-width is undefined (Line: 292)', which is the line with the .row() attribute:
#v_main_wrapper{
position:relative;
float:none;
.row(47);
&:after{
content: "";
display: table;
clear: both;
}
}
I would think that you would want this:
#total-width: #gridsystem-width; //leave this uncommented, to calculate width
//#total-width: 100%;

Less CSS - access part of class name and use in mixin

In Less, is it possible to access part of a class name and use within a mixin?
This will be best explained with an example:
I have a grid which i have declared as follows:
.columns (#columns) {
//code here to generate column widths
}
//This is where my problem is:
.column-1 {
.col (1)
}
.column-2 {
.col (2)
}
.column-3 {
.col (3)
}
// etc etc
Obviously there is a lot of repetitive code going on here. Ideally i would like to be able to not have to declare column-1 column-2 etc and have some way, regex perhaps, of parsing the class name, and using the value after the dash to automatically calculate the column width. I am almost certain twitter bootstrap is doing something similar but i cant understand it:
.spanX (#index) when (#index > 0) {
(~".span#{index}") { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
I think you'll understand that :
.columnX (#index) when (#index > 0) { // Guarded mixin: use this mixin when the condition is true (like an if statement)
(~".column-#{index}") { // outputs .column-0 as a class name
.col(#index); // for the contents, use the col mixin
} // which class you are doing
.columnX(#index - 1); // Recursive call to the same mixin, going down by 1
}
.columnX (0) {} // Default implementation so that when .columnX(0) is called, a matching mixin is found.
.col (#index) {
// actual css that will be applied to column-1 if #index is 1
width: #index * 10px; // for example
}
.columnX(3); // number of columns you want
Edit (missed the ; of .columnX(3); )
Edit Added more comments
All this should give the result :
.column-3 {
width: 30px;
}
.column-2 {
width: 20px;
}
.column-1 {
width: 10px;
}
This is essentially the same as #sherbrow's answer, but a little more concise and doesn't error. Consider this a long explanatory comment to support his correct answer - it's just a lot more than fits in a comment!
You'll use a LESS loop mixin like this as an intermediate helper, and then call it specifying the number of classes you want to generate. Here's how to do .column-1, .column-2, and .column-3. If say you wanted to go up to four columns: just do .columns(4) instead of .columns(3) [line 9]. To go up to five columns, just do .columns(5).
1 // we'll call this if we want to build columns
2 .columns(#i) when (#i > 0) {
3 .column-#{i} {
4 .col(#i)
5 }
6 .columns(#i - 1)
7 }
8 // for example, here we build columns 1-3
9 .columns(3);
which will compile to the result of
.column-1 {.col(1)}
.column-2 {.col(2)}
.column-3 {.col(3)}
(Your question assumes there's already a mixin .col(#x) so I'm assuming that too. See 4 for how to skip that extra step.)
Here's what's happening:
The whole first chunk [lines 1-7] just sits there until called.
.columns(3) [line 9] sends us to the .columns(#i) mixin [line 2], assigning the variable #i the value 3.
Because #i (3) is greater than 0 [line 2], we satisfy the guard and are allowed past the { [line 2].
.column-#{i} {...} [lines 3-5] is what this mixin will output.
#i is 3, so the output will be .column-3 {.col(3)}
the syntax #{i} is used to insert the variable's value as a string
If you don't need to use .col(#x) anywhere else, you could also just drop styles in here directly, e.g. (a là #sherbrow) .column-#{i} {width: #i * 10px}
And then the loop: after compiling lines 3-5, call this mixin again but with a different value [line 6]: .columns(#i - 1) ==> .columns(3 - 1) ==> .columns(2).
Back to the top [line 2]: #i, now 2, is greater than zero, so we're allowed in. output .column-2 {.col(2)} (where .col(2) is immediately compiled, so your compiled CSS actually reads .column-2 { the.col(2)styles })
And keep outputting and looping until #i isn't greater than 0 (i.e. stop after .columns(1)).

Resources