In Less, I can write:
.outer {
.inner {
color: red;
}
}
.test {
.outer .inner;
}
But when I write:
.outer {
&-inner {
color: red;
}
}
.test {
.outer-inner;
}
When I remove the .test, the .outer-inner output properly, but when I add it back, the compiler says
.outer-inner is undefined.
Is there anyway to re-use the styles of .outer-inner?
Calling a mixin whose selector is formed by concatenation is currently not possible with Less. However the same is possible for selectors formed at compilation time using interpolation (also referred to as dynamically formed selectors).
The below (interpolated/dynamically formed selector) would work fine.
#selector: .box;
#{selector}{
color: red;
.child{
color:blue;
}
}
.demo{
.box; /* will create both parent & child */
}
.container{
&.box{
background: black;
}
}
.demo2{
.container.box;
}
whereas, the following example will not work.
.container{
&-box{
color: blue;
}
}
.demo2{
.container-box; /* this will not work */
}
Currently, one work-around to the scenario in question is to create two separate Less files.
In the first file (test.less) add the below code and compile it into a CSS file.
.outer {
&-inner {
color: red;
}
}
In the second file, import the CSS created from the first file with the (less) directive and then call/re-use the mixin.
#import (less) "test.css";
.test {
.outer-inner;
}
Note: As mentioned in comments by seven-phases-max, this issue is similar to this item. However both these issues are not the same as extend will not work with both interpolated selector (dynamically formed) and concatenated selector.
Option 2: Another option would be to write a dummy mixin or a separate detached ruleset with common properties and make use of it like below.
#dummy: {color: red}; // detached ruleset
.outer{
&-inner{
#dummy();
}
}
.test{
#dummy();
}
or
.dummy() {color: blue}; // dummy mixin and would produce no extra selector in output as it has parentheses.
.outer{
&-inner{
.dummy;
}
}
.test{
.dummy;
}
I thought within Less you could do imports at the rule level?
e.g. given two Less files with identical variable names but different values
#import (reference) 'file1.less'
.myrule1
{
#import (reference) 'file2.less'
// use varA from file2
}
.myrule2
{
// use varA from file1
}
Is this not allowed, it doesn't seem to be in the latest Less version
Failing that can you do this
#import (reference) 'file2.less'
.myrule1
{
// use varA from file2
}
#import (reference) 'file1.less'
.myrule2
{
// use varA from file1
}
#import (reference) 'file2.less'
.myrule3
{
// use varA from file2 again
}
What am I trying to accomplish here? Kendo UI has multiple themes with colours for grids, headers, etc. Within my less file I want to make something like this
.BlackBasedThemes
{
#import one of the black themes
.MyDiv
{
background-color: #tooltipBackgroundColor;
}
// whole bunch of other stuff
}
.NonBlackBasedThemes
{
#import one of the not black themes
.MyDiv
{
background-color: #tooltipBackgroundColor;
}
// whole bunch of other stuff
}
And then within my code the body gets the NonBlackBasedThemes or NonBlackBasedThemes class. I can just add a MyDiv, etc class to a div and get the theme appropriate colour.
I thought within Less you could do imports at the rule level?
e.g. given two Less files with identical variable names but different values
When using lessc 2.4.0 (Less Compiler) [JavaScript] i can do:
black.less:
#tooltipBackgroundColor: black;
white.less:
#tooltipBackgroundColor: white;
Then the following code:
.BlackBasedThemes
{
#import "black";
.MyDiv
{
background-color: #tooltipBackgroundColor;
}
// whole bunch of other stuff
}
.NonBlackBasedThemes
{
#import "white";
.MyDiv
{
background-color: #tooltipBackgroundColor;
}
// whole bunch of other stuff
}
compiles into:
.BlackBasedThemes .MyDiv {
background-color: black;
}
.NonBlackBasedThemes .MyDiv {
background-color: white;
}
Indeed you do not need the reference keyword (but it should also work when using it). It is not easy to see what your problem is.
Notice that you can also import one of the files into the global scope:
#import "black"; // sets `#tooltipBackgroundColor` for the global scope
.BlackBasedThemes
{
.MyDiv
{
background-color: #tooltipBackgroundColor; // uses `#tooltipBackgroundColor` from the global scope
}
// whole bunch of other stuff
}
.NonBlackBasedThemes
{
#import "white";// sets `#tooltipBackgroundColor` for only the local scope
.MyDiv
{
background-color: #tooltipBackgroundColor;// uses `#tooltipBackgroundColor` from the local scope
}
// whole bunch of other stuff
}
In Less, I can write:
.outer {
.inner {
color: red;
}
}
.test {
.outer .inner;
}
But when I write:
.outer {
&-inner {
color: red;
}
}
.test {
.outer-inner;
}
When I remove the .test, the .outer-inner output properly, but when I add it back, the compiler says
.outer-inner is undefined.
Is there anyway to re-use the styles of .outer-inner?
Calling a mixin whose selector is formed by concatenation is currently not possible with Less. However the same is possible for selectors formed at compilation time using interpolation (also referred to as dynamically formed selectors).
The below (interpolated/dynamically formed selector) would work fine.
#selector: .box;
#{selector}{
color: red;
.child{
color:blue;
}
}
.demo{
.box; /* will create both parent & child */
}
.container{
&.box{
background: black;
}
}
.demo2{
.container.box;
}
whereas, the following example will not work.
.container{
&-box{
color: blue;
}
}
.demo2{
.container-box; /* this will not work */
}
Currently, one work-around to the scenario in question is to create two separate Less files.
In the first file (test.less) add the below code and compile it into a CSS file.
.outer {
&-inner {
color: red;
}
}
In the second file, import the CSS created from the first file with the (less) directive and then call/re-use the mixin.
#import (less) "test.css";
.test {
.outer-inner;
}
Note: As mentioned in comments by seven-phases-max, this issue is similar to this item. However both these issues are not the same as extend will not work with both interpolated selector (dynamically formed) and concatenated selector.
Option 2: Another option would be to write a dummy mixin or a separate detached ruleset with common properties and make use of it like below.
#dummy: {color: red}; // detached ruleset
.outer{
&-inner{
#dummy();
}
}
.test{
#dummy();
}
or
.dummy() {color: blue}; // dummy mixin and would produce no extra selector in output as it has parentheses.
.outer{
&-inner{
.dummy;
}
}
.test{
.dummy;
}
Is this possible?
file1.less
#import "file2.less"
#import "file3.less"
.caller {
.mixin();
width: #width;
height: #height;
}
.something-with-a-background {
background: url("#{images}/other-thing.png");
}
file2.less
#import "file3.less"
.mixin() {
#width: 100%;
#height: 200px;
background: url("#{images}/white-sand.png");
}
file3.less
#images: "../img";
And have these compile to:
allFiles.less
// file3.less
#images: "../img";
// file2.less
.mixin() {
#width: 100%;
#height: 200px;
background: url("#{images}/white-sand.png");
}
// file1.less
.caller {
.mixin();
width: #width;
height: #height;
}
.something-with-a-background {
background: url("#{images}/other-thing.png");
}
I just want the output to be the files concatenated together in the right order depending on the #import statements.
What I'm trying to do: I want to be able to add a less file in my dist directory so people can use mixins/vars/etc. by importing my less file. But in development I don't want them to all be in a single file. So I need a build that'll resolve the imports for me.
This is not necessary because you can simply do:
allFiles.less
#import "file3.less"
#import "file2.less"
#import "file1.less"
and that would have the same impact for anyone trying to do:
#import "allFiles.less"
as if they were all in that file. Thanks #JeffWhelpley!
I'm trying to use string interpolation on my variable to reference another variable:
// Set up variable and mixin
$foo-baz: 20px;
#mixin do-this($bar) {
width: $foo-#{$bar};
}
// Use mixin by passing 'baz' string as a param for use $foo-baz variable in the mixin
#include do-this('baz');
But when I do this, I get the following error:
Undefined variable: "$foo-".
Does Sass support PHP-style variable variables?
This is actually possible to do using SASS maps instead of variables. Here is a quick example:
Referencing dynamically:
$colors: (
blue: #007dc6,
blue-hover: #3da1e0
);
#mixin colorSet($colorName) {
color: map-get($colors, $colorName);
&:hover {
color: map-get($colors, #{$colorName}-hover);
}
}
a {
#include colorSet(blue);
}
Outputs as:
a { color:#007dc6 }
a:hover { color:#3da1e0 }
Creating dynamically:
#function addColorSet($colorName, $colorValue, $colorHoverValue: null) {
$colorHoverValue: if($colorHoverValue == null, darken( $colorValue, 10% ), $colorHoverValue);
$colors: map-merge($colors, (
$colorName: $colorValue,
#{$colorName}-hover: $colorHoverValue
));
#return $colors;
}
#each $color in blue, red {
#if not map-has-key($colors, $color) {
$colors: addColorSet($color, $color);
}
a {
&.#{$color} { #include colorSet($color); }
}
}
Outputs as:
a.blue { color: #007dc6; }
a.blue:hover { color: #3da1e0; }
a.red { color: red; }
a.red:hover { color: #cc0000; }
Sass does not allow variables to be created or accessed dynamically. However, you can use lists for similar behavior.
scss:
$list: 20px 30px 40px;
#mixin get-from-list($index) {
width: nth($list, $index);
}
$item-number: 2;
#smth {
#include get-from-list($item-number);
}
css generated:
#smth {
width: 30px;
}
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#lists
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#list-functions
Anytime I need to use a conditional value, I lean on functions. Here's a simple example.
$foo: 2em;
$bar: 1.5em;
#function foo-or-bar($value) {
#if $value == "foo" {
#return $foo;
}
#else {
#return $bar;
}
}
#mixin do-this($thing) {
width: foo-or-bar($thing);
}
Here's another option if you're working with rails, and possibly under other circumstances.
If you add .erb to the end of the file extension, Rails will process erb on the file before sending it to the SASS interpreter. This gives you a can chance to do what you want in Ruby.
For example: (File: foo.css.scss.erb)
// Set up variable and mixin
$foo-baz: 20px; // variable
<%
def do_this(bar)
"width: $foo-#{bar};"
end
%>
#target {
<%= do_this('baz') %>
}
Results in the following scss:
// Set up variable and mixin
$foo-baz: 20px; // variable
#target {
width: $foo-baz;
}
Which, of coarse, results in the following css:
#target {
width: 20px;
}
I came across the need to reference a colour dynamically recently.
I have a _colours.scss file for every project, where I define all my colours once and reference them as variables throughout.
In my _forms.scss file I wanted to setup button styles for each colour available. Usually a tedious task. This helped me to avoid having to write the same code for each different colour.
The only downside is that you have to list each colour name and value prior to writing the actual css.
// $red, $blue - variables defined in _colours.scss
$colours:
'red' $red,
'blue' $blue;
#each $name, $colour in $colours {
.button.has-#{$name}-background-color:hover {
background-color: lighten($colour, 15%);
}
}
I needed to use dynamic color values in sass variables.
After lots of search, I applied this solution:
In application.html.erb:
<style>
:root {
--primary-color: <%= current_client.header_color %>;
--body-color: <%= current_client.footer_color %>;
}
</style>
In variables.sass:
$primary: var(--primary-color);
And boom you are good to go!
Reference: https://medium.com/angular-in-depth/build-truly-dynamic-theme-with-css-variables-539516e95837
To make a dynamic variable is not possible in SASS as of now, since you will be adding/connecting another var that needs to be parsed once when you run the sass command.
As soon as the command runs, it will throw an error for Invalid CSS, since all your declared variables will follow hoisting.
Once run, you can't declare variables again on the fly
To know that I have understood this, kindly state if the following is correct:
you want to declare variables where the next part (word) is dynamic
something like
$list: 100 200 300;
#each $n in $list {
$font-$n: normal $n 12px/1 Arial;
}
// should result in something like
$font-100: normal 100 12px/1 Arial;
$font-200: normal 200 12px/1 Arial;
$font-300: normal 300 12px/1 Arial;
// So that we can use it as follows when needed
.span {
font: $font-200;
p {
font: $font-100
}
}
If this is what you want, I am afraid as of now, this is not allowed