Why LESS compiler wont recognize classes generated by zero iterator? - css

My CSS sheet is required to reset some Bootstrap 3 preset values to make my desired style, thus I need utility classes like .m-b-0 which set margin-bottom:0px.
But Less compiler I was using in grunt wont recognize classes .m-b-0 generated by loop when I nested them in other class, but classes like .m-b-1 and .m-b-10 will be recognized correctly.
The loop code in my Less files is like this:
.m-loop (#i) when (#i <= #iterations) {
.m-#{i}{
margin: ~"#{i}px";
}
.m-h-#{i}{
margin-left: ~"#{i}px";
margin-right: ~"#{i}px";
}
.m-v-#{i}{
margin-top: ~"#{i}px";
margin-bottom: ~"#{i}px";
}
.m-l-#{i}{
margin-left: ~"#{i}px";
}
.m-r-#{i}{
margin-right: ~"#{i}px";
}
.m-t-#{i}{
margin-top: ~"#{i}px";
}
.m-b-#{i}{
margin-bottom: ~"#{i}px";
}
.m-loop(#i + 1);
}
.m-loop(0);
and I use the classes supposed to be generated in the same less file like this:
.panel{
.m-b-0;
}
Then my compiler throwed
Running "less:production" (less) task
>> NameError: .m-b-0 is undefined in ../css/less/stylesheet.less on line 237, column 3:
>> 236 div.panel{
>> 237 .m-b-0;
>> 238 }
Warning: Error compiling ../css/less/stylesheet.less Used --force, continuing.
Is this forbidden to use zero valued iterator in Less compiling or where did I take it wrong? Thanks!

Looks like there might be a syntax error. I don't see an opening brace on this line:
.m-t-#{i}
See if that helps.

Related

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};
}

SASS Customize Class Names with Variables

Is there any way to customize the variables in SASS?
For example:
.m-b-{$number} {
margin-bottom: $number;
}
If I give class="m-b-50" to an element, it should take margin-bottom 50. I just want to know if it is possible with SASS.
Yes it is possible with the help of variable interpolation or variable substitution which uses #{} for variable substitution in SASS and mixins which is a block of code just like function.
Interpolation is the process of evaluating an expression or a string containing one or more variables, yielding a result in which the variables are replaced with their corresponding values.
Simple example of interpolation and set values to the css property in SASS:
$number:60;
$n: 20px;
.m-b-#{$number}{
margin-bottom: #{$number}px;
margin-top: $n;
}
To create customize class names, will use mixins:
#mixin margin-class($side, $number) {
$firstLetter: str-slice($side, 0, 1);
.m-#{$firstLetter}-#{$number}{
margin-#{$side}: #{$number}px;
}
}
$margins: (10, 20);
$sides: ("top", "right", "bottom", "left");
#mixin generate-margin(){
#each $margin in $margins{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
#include generate-margin();
Here, generate-margin() will get executed which will call margin-class() for each $margins and $sides, and will generate the below CSS classes:
.m-t-10 {
margin-top: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-t-20 {
margin-top: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
That's the one way when you want only for specific values, but if you want to create margin class for 0-20, you can loop thru 0 to 20 as shown below:
#mixin generate-margin(){
#for $margin from 1 through 20{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
For anyone else facing this issue, here is how one can achieve this:-
#for $i from 1 through 10 {
.mb-#{$i} {
margin-bottom: #{$i}rem;
}
}
The answer is: no it is not possible. SASS is just a language to pre-generate CSS for you. There is no on-demand, dynamic creation of classes triggered by the contents of your HTML markup. When it comes time for the browser to render your HTML and apply your specified classes, it is still just using CSS. I.e. if you assign class="m-b-50" to an element, the class .m-b-50 must already be explicitly defined somewhere. As noted in the other answers, SASS can make it easier to generate a bunch of pre-defined classes but you must know which values you want to support up front.
Now, you could generate classes for some very large, all-inclusive range like -1000 to 1000 to effectively support all values you might ever try to use and it would seem to do what you wanted, but you would be forcing your users to download a larger CSS file with, most likely, a large percentage of it being unused CSS which is wasteful and can be inconsiderate in a world of paid & limited data plans.

scss error - &:not(": expected ")", was "'.full')

I'm a bit confused with the following errors. I have no experience with SCSS.. it's a file a subcontracting front-end designer company gave us.
I am attempting to compile using KOALA that seemed to be an easy open source free windows sass compiler.
Errors:
assets\sass\main.scss
Error: Invalid CSS after "&:not(": expected ")", was "'.full')"
on line 23 of assets/sass/components/_last-update.scss
from line 13 of assets/sass/_components.scss
from line 8 of assets\sass\main.scss
Use --trace for backtrace.
The specific code causing the error seems to be:
#media (min-width: $bp-medium) {
&:not('.full') { max-width: 60%; }
margin-bottom: $vertical-spacing-medium;
}
Specifically:
&:not('.full') { max-width: 60%; }
I tried removing that part all together and it seems to work but I havn't understood yet the SCSS/SASS syntax and why it would not be working. I've only been explosed to this language for 30' now.
I think the problem are the hypens in the selector. e.g.
&:not('.full') { max-width: 60%; }
should become
&:not(.full) { max-width: 60%; }
as defined by the specification: https://developer.mozilla.org/de/docs/Web/CSS/:not

Less media queries don't seem to be compiled

I have a media queries in less file, code:
#phoneMin: 0px;
#tabletMin: 768px;
#smallDesktopMin: 992px;
#phoneMax: (#tabletMin - 1);
#tabletMax: (#smallDesktopMin - 1);
#atLeastTablet: ~"only screen and (min-width: #{tabletMin})";
When I #import this file into another less file and utilize my media queries like so:
.container {
#media #atLeastTablet {
margin-top: 160px;
}
}
Not only does the media query just not work, but it also doesn't seem to compile into the resulting css file, which reads like this:
.container {
margin-top: 160px;
}
EDIT I attempted to run my server and I'm now getting the following error message: "Unexpected token(1:0)" referring to the first character (period) in one of my less files:
.blogPost {
^ .logo {
}
}
The error is being thrown during the babel 6 compilation process. Here is the stack trace:
at Parser.pp.raise (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/location.js:22:13)
at Parser.pp.unexpected (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/util.js:91:8)
at Parser.pp.parseExprAtom (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:510:12)
at Parser.<anonymous> (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/plugins/jsx/index.js:404:22)
at Parser.parseExprAtom (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/plugins/jsx/index.js:404:22)
at Parser.pp.parseExprSubscripts (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:265:19)
at Parser.pp.parseMaybeUnary (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:245:19)
at Parser.pp.parseExprOps (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:176:19)
at Parser.pp.parseMaybeConditional (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:158:19)
at Parser.pp.parseMaybeAssign (/usr/local/lib/node_modules/babel-cli/node_modules/babylon/lib/parser/expression.js:121:19)
It turns out the issue is in trying to import my less in a subscript vs my webpack entry point. Moving the import here fixed the issue.

How to throw an error in the LESS compiler

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;
}
}

Resources