LESS mixins with multiple parameters raises syntax error - css

I wrote this followed the documentation of the LESS website, the mixins part, which I thought would work but raises a syntax error:
SyntaxError: properties must be inside selector blocks, they cannot be in the
root. in less/style.less on line 3, column 3:
2 .bg (#x; #y) {
3 background-position: unit(#x, px) unit(#y, px);
4 }
Here is the Less:
.bg (#x; #y) {
background-position: unit(#x, px) unit(#y, px);
}
.mydiv (#x:0; #y:-52; #width:300px; #height: 155px) {
.bg(#x, #y);
width: #width;
height: #height;
opacity: 1;
}
.mydiv()
also if I just use only multiple parameters it cause the same error:
SyntaxError: properties must be inside selector blocks, they cannot be in the
root. in less/style.less on line 14, column 3:
13 .mydiv(#width:300px; #height: 155px) {
14 background-position: 0px -52px;
15 width: #width;
Less:
.mydiv (#width:300px; #height: 155px) {
background-position: 0px -52px;
width: #width;
height: #height;
opacity: 1;
}
.mydiv()
I don't know what's wrong with it...please help...
quote: i'm using less with grunt-contrib-less, and less 1.4.2, in windows 8.1 x64.

You're calling .mydiv() outside the scope of CSS block, that would (hypothetically) would output incorrect CSS. something like:
/* some arbitrary css: */
body { font-family: Arial; }
a { text-decoration: underline; }
/* your mixin (invalid): */
background-position: 0px -52px;
width: #width;
height: #height;
opacity: 1;
You have to wrap the mixin call within a CSS block, something like:
.bg (#x; #y) {
background-position: unit(#x, px) unit(#y, px);
}
.mydiv (#x:0; #y:-52; #width:300px; #height: 155px) {
.bg(#x, #y);
width: #width;
height: #height;
opacity: 1;
}
.myClassThatUsesMyDiv
{
.mydiv()
/* can be with some other directives: */
background-color: transparent;
}

Using mixin containing attributes outside any other elements will result in incorrect CSS, and the error you've come across is because LESS compiler wants to prevent that.
Q: So how do I use my mixin?
A: Make sure you understand what is a mixin definition, and what is a mixin call.
I'll use simplified examples to clearly explain this.
This is mixin definition:
.sample-mixin (#color; #width: 100px) {
color: #color;
display: block;
width: #width;
}
To use such a mixin, you just call it like a function:
.sample-mixin(#eeffee); // this line is actual mixin call
Mixin call is evaluated to whole mixin content (with variables evaluated):
color: #eeffee;
display: block;
width: 100px;
Q: When calling mixins outside other block is incorrect?
A: If your mixins contains at least one attribute:
.sample-mixin (#color) {
color: #color;
}
Then calling it outside block:
.sample-mixin(#eeffee);
Results in incorrect CSS:
color: #eeffee;
But calling it inside block:
p {
.sample-mixin(#eeffee);
}
Is allright, as it results in correct CSS:
p {
color: #eeffee;
}
Q: When calling mixins outside other block is correct?
A: Only if your mixins contains nothing but blocks:
.sample-mixin (#color) {
body {
color: #color;
}
}
Then calling it outside block:
.sample-mixin(#eeffee);
Results in correct CSS:
body {
color: #eeffee;
}
Sidenote: having blocks inside mixins is not a good practice, as it can effectively confuse readers and results in higher coupling inside your CSS.

Related

Sass mixin with nested ampersand prefix

What's the best way to accomplish the ability to use class prefixes using a mixin with placeholder selectors.
To elaborate, say I have a box class that has 3 sizes but I would like to have the option of having it a different color.
My base classes would be:
.box
.box-md
.box-sm
If I wanted any of the base class boxes to be green, I would like to be able to specify as such:
.box-green
.box-md-green
.box-sm-green
How would I be able to do so in as DRY a method as possible?
Similar to this answer but using mixins AND placeholder extends: SCSS, how to #extend Nested ampersand "prefix"?
Here's what I put together so far (which doesn't work)
HTML:
<div class="box"></div>
<div class="box-green"></div>
<div class="box-sm"></div>
<div class="box-sm-green"></div>
CSS (SCSS):
// Main style placholder as mixin
#mixin box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
&-green {
background-color: green;
}
}
// Placeholders
%box {
#include box;
}
%small-box {
#include box;
width: 100px;
height: 100px;
}
// Class Definition
.box { #extend %box; }
.box-sm { #extend %small-box; }
Pen: https://codepen.io/Aricha_MW/pen/xxKZWbV
This isn't a complete answer but does pose as a solution to the problem:
Our champions are the #at-root directive and interpolation here. The solution requires both the use of mixins and placeholder selectors and is a little messy.
If we set up our placeholder selector styles:
%box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
}
%small-box {
#extend %box;
width: 100px;
height: 100px;
}
Then we can let mixins do the rest of the work for us.
First we define our mixin for the variation we want:
#mixin green-bg($selector, $root) {
// Takes element out of any nesting
// Then we interpolate our argument variables
#at-root{
#{$root}-green {
// We can't set a placeholder as an argument so we'll just borrow the string and append the placeholder definer '%'
#extend %#{$selector};
background-color: green;
}
}
}
Then we define our mixins that will help us define our classes
#mixin box($parent) {
#extend %box;
#include green-bg(box, #{$parent});
}
#mixin small-box($parent) {
#extend %small-box;
#include green-bg(small-box, #{$parent});
}
When we define our classes they'll look clean like so:
.box { #include box(&); }
.box-sm { #include small-box(&); }
Here's what the final product looks like in action: https://codepen.io/Aricha_MW/pen/oNvxjEw
Edit: 08/15/2019 - Much cleaner version here:
https://codepen.io/Aricha_MW/pen/mdbPVXY

Use sass parent (ampersand) selector with fixed root class

In the following code example I generate two squares that ideally should turn red.
The first div .with-root currently stays blue, the second div .without-root turns red. I expect this behaviour, but don't see a proper solution to turn the .with-root div red as well.
Note the difference in the scss file: the first div works with a fixed parent selector, the second one doesn't have a parent. For CSS specificity I need to work with the .with-root {} wrapper.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
background: red;
}
}
}
}
.without-root {
&__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
display: block;
width: 5rem;
height: 5rem;
background: red;
}
}
}
}
The codepen can be found here: https://codepen.io/studiotwist/pen/OzMOmr
Well now that I hopefully understood your question I deleted my wrong idea before and the following solution should work.
Maybe there could be a logic erorr. You have actually three class definitions of .with-root__element and two of them are extended with --red and __item, but the 3rd one is however an extra class which comes in conflict with the other two. You're basically concatenating the endings --red and __item with the parent selector *__element. Also, the --red class is nested inside the *__element one without ending in your CSS but in HTML it is not. *__element and *__element--red are attached in the same HTML tag.
DEBUG
Only showing the first DIV.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red {
//#error &; // this reference contains the entire document root including the root element .with-root which is wrong
#{&} &__item {
//#error #{&} &__item; // this is a wrong concatenation plus it takes the entire root with it
background: red; // thus, this won't render
}
}
}
}
Debug in action # Sassmeister
POSSIBLE FIX
#mixin bg($bg) {
width: 5rem;
height: 5rem;
background: $bg;
}
.with-root__element {
#include bg(blue);
$this: &;
#at-root {
.with-root {
#{$this}--red #{$this}__item {
#include bg(red);
}
}
}
}
.without-root {
&__element {
#include bg(blue);
&--red &__item {
#include bg(red);
}
}
}
Fork
#at-root is a directive which is useful for your issue as it basically crops the nesting level of the selector and styles can be defined inside the root-body by referencing the parent selector instead of the entire root. So I added a variable $this which will cache the reference. display: block is not needed as div elements have it by default. Sorry about the mixin, it's a habit. --red and __item have now the refence selector *__element.
#at-root Documentation

Nested mixins in LESS behaviour

Would it be possible in LESS to have a mixin nested inside another one so that the former gets called only when the element is child of an element with the latter mixin?
I know, confusing, here is a simple example (not working code, just concept):
LESS
.foo(#x) {
width: #x;
.foo(#y) {
width: #y/#x;
}
}
.a {
.foo(20px);
.b {
.foo(2);
}
}
Output CSS
.a {
width: 20px;
}
.a .b {
width: 10px;
}
When I do this, calling .foo(2) on .b gives compiles to width: 2.
Is this supposed to be like this by design, or am I getting something wrong in the syntax? Also, am I approaching the problem from a completely wrong angle and there is perhaps a much simpler solution that I am not considering?
EDIT
Ok, apparently that was fixed with the newest versions of LESS, what I am trying to achieve, though, is slightly more complicated than the minimal example I gave above.
Basically what I would like to happen is that every .foo which is a child of another element with the .foo mixin would take its parent variable for calculation, so, ideally
LESS
.foo(#x) {
width: #x;
.foo(#y) {
width: (#x/#y);
}
}
.a {
.foo(100px);
.b {
.foo(2px);
.c {
.foo(5px);
/* ...and so on */
}
}
}
Output CSS
.a {
width: 100px;
}
.a .b {
width: 50px;
}
.a .b .c {
width: 10px;
}
What I get is, instead:
.a .b .c {
width: 50px;
}
I tried to modify the LESS as follows:
.foo(#x) {
width: #x;
.foo(#y) {
#x: (#x/#y)
width: (#x);
}
}
But I get a syntax error for recursive variable definition. Apparently LESS doesn't allow for definitions like:
#a: 1;
#a: (#a+1);
I think the main problem here would be the #y/#x.
Try #x/#y and it should give something more expected ;-)
As the nested mixins get interpreted different throughout different less implementations I will now split the answer in two parts:
1. Sollution that worked on less2css.org with LESS >1.3.1 (using your nested mixins)
Otherwise I think the above code actually does what you want on less2css.org.
Just as a notion in LESS 1.4, you need to be careful with the math as by default it needs to be in brackets.
If you now just call such a mixin on nonnested rules, like
.b {
.foo(#y);
}
you neen to use units in the input variable, or ad unit() into your mixin, otherwise you will only get the number you put in, 2 for example:
.foo(#x) {
width: unit(#x,px);
.foo(#y) {
width: (#x/#y);
}
}
.a {
.foo(20px);
.b{
.foo(2);
}
}
.c {
.foo(2);
}
will output CSS:
.a {
width: 20px;
}
.a .b {
width: 10px;
}
.c {
width: 2px;
}
You could get even fancier, and check with guards if the attribute in the subclass has pixles as a unit, so that you can also nest classes where you don't pass a factor:
.foo(#x) {
width: #x;
.foo(#y) when (ispixel(#y)) {
width: #y;
}
.foo(#y) when not (ispixel(#y)) {
width: (#x/#y);
}
}
However testing this nested mixin solution appears to work only on less2css.org, but not on jsbin.com, lesstester.com and some other services [where you need to call the nested (second) level of mixin with .foo .too, to apply the second level of styling from the mixin].
So I propose an alternative approach, that I tested and it seems to work on all mentioned pages using less-css compilers with less >1.2.
2. Solution using guards with ispixel that should work on most LESS >1.2 installations
Instead of your nested mixin you could build two mixins, that are based on guards checking for pixels as the unit.
if attribute #x is in pixels => return width:#x; and assign #x to variable #width
if attribute #x is not in pixels => return width:#width/#x; (note: #width needs to be previously assigned by calling the mixin with the #x in px first)
example LESS:
.foo(#x) when (ispixel(#x)) {
width:#x;
#width:#x;
}
.foo(#x) when not (ispixel(#x)) {
width: (#width/#x);
}
.a, .b {
background-color: #ddd;
height: 100px;
}
.a {
.foo(100px);
.b {
background-color: red;
.foo(2);
}
}
th output CSS:
.a, .b {
background-color: #ddd;
height: 100px;
}
.a {
width: 100px;
}
.a .b {
background-color: red;
width: 50px;
}
Differs from your approach but is perhaps a bit more straight forward, and seems to work well.
Edit:
So as you don't want to distinguish between input with unit and input without unit, I can only think of calling a two parametric mixin, where one parameter is used for the base (width in your case) and the second as a factor that defaults to 1. And this you can call now recursively as many times as you want.
LESS:
.foo(#x,#y:1){
width:unit(#parent,px);
#parent:(#x/#y);
}
.a {
.foo(100px);
.b{
.foo(#parent,2px);
.c{
.foo(#parent,5px);
.d{
.foo(#parent,0.05);
}
}
}
}
output CSS:
.a {
width: 100px;
}
.a .b {
width: 50px;
}
.a .b .c {
width: 10px;
}
.a .b .c .d {
width: 200px;
}
So now it does not matter what unit the input has because you can assign the desired unit for the output in the mixin. When you call the mixin it overwrites the #parent variable for the current scope, which gets then inherited to nested rules, where you can use it as the width parameter #x when calling the mixin again. This should give you the desired result.

LESS CSS - Change variable value for theme colors depending on body class

Getting to grips with LESS here but one thing is still a little unclear.
Lets say I have multiple color themes for my website, controlled by a class on the body tag. From this I can redefine the various colors for each element within each theme. Easy enough but fairly time consuming if I have a lot of elements to change... and a lot of themes. Every time I add a new theme I need to write out all the selectors again, with different color values.
I am basing my working so far on another post I found:
LESS.css variable depending on class
... However it still seems overly complicated for what I want to do in that I still have to write out all the selectors and include the mixin before dropping in the same CSS with the color variable.
I have created a CODEPEN HERE
I'd appreciate it if anyone had time to take a little look and advise me how I could approach this differently or how I could streamline this process.
Many thanks to anyone who helps out :)
Assuming you remain with wanting to theme it within one style sheet (and not multiple sheets as cimmanon noted in the comments), and assuming you are using LESS 1.3.2+, then the following code works to reduce the amount of duplication by setting up a loop through the classes that need theme changes.
Note that this does not work on Codepen (it is throwing an error uncaught throw #, perhaps because they are running an earlier version of LESS), but you can see it compiling correctly by putting the code into LESS's compiler.
LESS (based off your Codepen code with an added theme for demo)
//////////////////////////////////////////////////////
// CONSTANTS
#lightColour: #fff;
#darkColour: #000;
#lightBg: #fff;
#darkBg: #000;
#numberOfThemes: 3; //controls theme loop
//////////////////////////////////////////////////////
// MIXINS
//Theme Definitions by parametric mixin numbers (1), (2), etc.
.themeDefs(1) {
#lightColour: #f00;
#darkColour: #fff;
#lightBg: #f00;
#darkBg: #fff;
}
.themeDefs(2) {
//inverse of 1
#lightColour: #fff;
#darkColour: #f00;
#lightBg: #fff;
#darkBg: #f00;
}
.themeDefs(3) {
#lightColour: #cfc;
#darkColour: #363;
#lightBg: #cfc;
#darkBg: #363;
}
.curvy {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
//////////////////////////////////////////////////////
// GENERAL STYLING
* {padding: 0;margin: 0;}
html {text-align: center;}
h2 {padding: 20px 0;}
.box {
.curvy;
color: #lightColour;
background: #darkBg;
display:inline-block; width:10%; padding:20px 5%; margin:0 1% 20px 1%;
}
//////////////////////////////////////////////////////
// THEME BUILDING
.buildThemes(#index) when (#index < #numberOfThemes + 1) {
.theme-#{index} {
.themeDefs(#index);
color: #lightColour;
background: #darkBg;
.box {
color: #darkColour;
background: #lightBg;
}
}
.buildThemes(#index + 1);
}
//stop loop
.buildThemes(#index) {}
//start theme building loop
.buildThemes(1);
CSS Output (only showing the looped theme css for brevity)
.theme-1 {
color: #ff0000;
background: #ffffff;
}
.theme-1 .box {
color: #ffffff;
background: #ff0000;
}
.theme-2 {
color: #ffffff;
background: #ff0000;
}
.theme-2 .box {
color: #ff0000;
background: #ffffff;
}
.theme-3 {
color: #ccffcc;
background: #336633;
}
.theme-3 .box {
color: #336633;
background: #ccffcc;
}

Compass sprite generating too much CSS classes

I am using compass to generate sprite images. And I have MANY sprite icons, and it is generating too much CSS code (too many class selectors for the background image). So lets analyze the compass sprite code:
as you can see here http://compass-style.org/help/tutorials/spriting/
#import "my-icons/*.png";
#include all-my-icons-sprites;
Will generate:
.my-icons-sprite,
.my-icons-delete,
.my-icons-edit,
.my-icons-new,
.my-icons-save { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
If you see I use this way: <div class="my-icons-sprite my-icons-delete"></div>
I want Compass to generate this code:
.my-icons-sprite { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
Else each new image, it'll add for background and background position. Causing too many selectors.
Is there a configuration for that?
Thanks
Have you tried this snippet for Compass?
$icons: sprite-map("icons/*.png");
i{
background: $icons;
display: inline-block; // or block
}
#each $i in sprite_names($icons){
.icn-#{$i}{
background-position: sprite-position($icons, $i);
#include sprite-dimensions($icons, $i);
}
}
This example uses the <i></i>-tag with a class containing the prefix icn- combined with the filename of the separate .png-files in your icons-folder. Like this:
<i class="icn-delete"></i>
The generated CSS looks like this:
i {
background: url('/path/to/generated/spritemap/my-icons-xxxxxxxxxxx.png');
display: inline-block;
}
.icn-delete {
background-position: 0 0;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
.icn-edit{
background-position: 0 -32px;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
.icn-new {
background-position: 0 -64px;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
...
..
.
Still, I haven't quite figured out how to use this in combination with Compass' Magic Selectors.
Magic Selectors works very nice when you need different states (:hover, :active, :target). All you have to do is name your files like this: filename_state.png (delete_hover.png, delete_active.png etc). Compass' Magic Selectors then automatically generates css for :hover, :active and :target (delete:hover, delete_hover and delete-hover). This way you are quite free to choose how you would represent a state-change.
If you, in my first example, has filenames with the postfix for hover/ active states, the snippet only writes CSS like this:
.icn-edit_hover {
background-position: -32px -32px;
height: 32px;
width: 32px;
}
I'd really like to have it print this:
.icn-edit:hover, .icn-edit_hover, .icn-edit-hover{
background-position: 0 -32px;
height: 32px;
width: 32px;
}
like the traditional Compass' Magic Selectors does. Any idea?
In my opinion, it seems like the best of both worlds (less HTML and CSS) would be to have this code (using an attribute selector for the image):
HTML
<div class="my-icons-delete"></div>
CSS
[class^="my-icons-"] { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
Unfortunately, I do not know how to get Compass to export like that. However, unless you are using Compass dynamically rather than just to build your back end static css, you could just change it once generated.
For anyone looking to the answer to ScottS question.
How can I use a css selector for anything starting with a baseclass
Try this:
http://codepen.io/Acts7/pen/nwsEb
I'm pasting the code below.
the spriteGen mixin requires two parameters
1) the baseclass you want to use (in ScottS case --- "myicons"
2) the second parameter is the folder location
Also DONT forget the "." before #{$mySpriteBaseClass}.
Otherwise you get >> myicons-home_icon{background-position:...}
(notice no . for class name selector)
// _custom.scss
// ---------------------------------------------------------
// Sprite Generation
--------------------- */
#include spriteGen('sprites','sprites');
// _mixins.scss
// ---------------------------------------------------------
// Sprite Generation Mixin with options
#mixin spriteGen($mySpriteBaseClass:'.spritebc',$mySpriteFolder:'sprites'){
$mySprites:$mySpriteFolder + "/*.png";
$spritefoldername-map: sprite-map($mySprites,
$spacing: 10px,
$layout: vertical
);
// if using base class as starter for sprite name class
[class^="#{$mySpriteBaseClass}"]{
/*// if using a separate base class
.#{$mySpriteBaseClass}{*/
// TODO:
// Add if/else to set width globally
// or let spriting assign it per each
//width: 48px;
//height: 48px;
display: inline-block;
vertical-align: middle;
background: $spritefoldername-map no-repeat;
}
#each $sprite in sprite_names($spritefoldername-map) {
// if using sprite base class as prefix to full sprite class name
.#{$mySpriteBaseClass}-#{$sprite} {
/*// if using separate base class and sprite name class
.#{$sprite} {*/
background-position: sprite-position($spritefoldername-map, $sprite);
#include sprite-dimensions($spritefoldername-map, $sprite);
}
}
}
What's wrong with the current output?
You can already assign my-icons-delete/edit/new/save only, this is semantic enough - it already says it's an icon and it's a delete icon.
This is what I'm currently doing, it requires Sass 3.3 though:
$icons: sprite-map('icons/*.png');
.icon {
background: $icons;
display: inline-block;
vertical-align: middle;
}
#each $i in sprite_names($icons) {
$underscore: str-index($i, _);
#if ($underscore < 1) {
.icon--#{$i} {
background-position: sprite-position($icons, $i);
#include sprite-dimensions($icons, $i);
}
} #else {
$prefix: str-slice($i, 0, $underscore - 1);
$postfix: str-slice($i, $underscore + 1);
.icon--#{$prefix}:#{$postfix} {
background-position: sprite-position($icons, $i);
}
}
}
I'm using BEM here so it assumes you'll use this like <i class="icon icon--star></i>, so if if you have a "star.png" and "star_hover.png" images it'll generate .icon--star and .icon--star:hover class names.

Resources