In LESS, how to use a variable inside another? - less

Variable #screen-xs-max is a Bootstrap 3 variable. In my mixin, I'd like to use #class variable to get the Bootstrap variable, dynamically.
Here is a not working solution:
.make-btn-block (#class) {
#media (max-width: #screen-#{class}-min) {
// Code
}
}
.make-btn-block(xs);
Another not working solution:
~"#{#screen-#{class}-min}"

See referencing variables by name. E.g.:
.make-btn-block(#device) {
#max: "screen-#{device}-max";
#media (max-width: ##max) {
color: red;
}
}

Related

Less, multiple imports

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
}

Set min and max breakpoints with single variable with LESS?

Im trying set a media query breakpoint with LESS so I can have something like this:
#media (max-width: 1000px) {
// something
}
#media (min-width: 1001px) {
// something
}
I want a single place to control the breakpoint. The following isnt working. Is this just a syntax issue or am I going about this the wrong way?
#breakpoint: 1000;
#breakpoint-max: #breakpoint;
#breakpoint-min: #breakpoint + 1;
#media (max-width: #breakpoint-max) {
// something
}
#media (min-width: #breakpoint-min) {
// something
}
I not sure why the media query to try to compile should not work, except for the missing unit as mentioned by #SLaks already.
In you example situation you can apply 'mobile first':
#breakpoint: 1000px;
#breakpoint-max: #breakpoint;
#breakpoint-min: (#breakpoint + 1);
//something
p {
color:red;
#media (min-width: #breakpoint-min) {
// something
color:green;
}
}
The above compiles into the code shown below by default:
p {
color: red;
}
#media (min-width: 1001px) {
p {
color: green;
}
}
The color is always red, unless the screen width is wider that 1000 px.
Also notice the brackets in #breakpoint-min: (#breakpoint + 1);. The requirement of brackets in this case depend on the sm option:
-sm=on|off Turns on or off strict math, where in strict mode, math.
--strict-math=on|off Requires brackets. This option may default to on and then
be removed in the future.
You never specified a unit.
You need to set your variable to 1000px.

Is there any (good) way to extend a class within a mixin, and then use that mixin within a media query, using Less?

I've been working on building out some Less files to help speed up my CSS workflow, and also to help produce more efficient, cleaner CSS.
The way I see it:
Mixins are a great way to help speed up the workflow, but they have the drawback of potentially making the outputted CSS longer than necessary.
Extending classes is the ideal solution for ensuring the amount of duplicate style declarations is minimized, helping clean that up...
So, to help balance things out I wrote out a set of standard, commonly used styles, using dummy classes (they are stored in a file which is imported by reference, so the styles are only output if they get extended).
I set all of my Mixins to extend these classes wherever possible, which worked great for the most part.
However, I realized my pitfall once I got to my media queries... I can't extend those classes within the media query, which would be fine normally, I would just remember not to do so.. But since the Mixins also now use my extends, I can now no longer use them inside media queries either.
I'm not willing to avoid using the Mixins inside of the media queries because of this, but I'd really love to be able to find a way to keep extending classes within them to keep my output as clean as possible.
The only idea I've thought of so far is to add an extra parameter to every Mixin to specify wether it should extend or not, but that's less than ideal.
My hope is that someone can come up with a much more clever solution, that would allow me to maintain the benefit of Mixins which extend base style classes, but also maintain easy usability, without over complicating things. Might be a tall order, but here's hoping.
In case my explanation was hard to follow, this is what I would have hoped to be able to do, but is not currently possible:
Ideal Input
// extensions.less
.block {
display: block;
}
// mixins.less
#import (reference) "extensions";
.mixin {
&:extend(.block);
margin: auto;
}
// styles.less
#import "mixins";
.element1 {
.mixin();
}
.element2 {
.mixin();
}
#media only screen and (max-width: 768px) {
.element3 {
.mixin();
}
.element4 {
.mixin();
}
}
Ideal Output
// styles.css
.element1, .element2 {
display: block;
}
.element1 {
margin: auto;
}
.element2 {
margin: auto;
}
#media only screen and (max-width: 768px) {
.element3, .element4 {
display: block;
}
.element3 {
margin: auto;
}
.element4 {
margin: auto;
}
}
In short, yes, currently it is somewhat possible but requires some additional wrapping for a top level classes:
// extensions.less
.block {
display: block;
}
// mixins.less
#import (reference) "extensions";
.mixin() {
&:extend(.block);
margin: auto;
}
// styles.less
#media all { // !
#import "mixins";
.element1 {
.mixin();
}
.element2 {
.mixin();
}
}
#media only screen and (max-width: 768px) {
#import (multiple) "mixins";
.element3 {
.mixin();
}
.element4 {
.mixin();
}
}
.element1 and .element2 (and any other class to extend .block) have to be put into #media all because currently:
Top level extend matches everything including selectors inside nested media
So if .element1 and .element2 stay in the global scope they leak into every other #media .block declaration.
(Hmm, actually for me this "top level extend matches everything" thing looks questionable and contradicts another "extend inside a media declaration should match only selectors inside the same media declaration" rule (obviously because global scope = #media all thus they should work identically)).

LESS condition based on CSS class to set a LESS variable

I need to set a Less variable to match the website's active theme, ie, each theme has a different color.
I'd like to set #themeColor to the right color, based on the HTML's body CSS class that defines the theme.
For example:
body.themeBlue { #themeColor: blue; }
body.themeRed { #themeColor: red; }
This way I'd only need to use the #themeColor variable inside the other Less files.
Can anyone help?
According to this (http://www.lesscss.org/#-scope) it is possible to do something like that, but I can't make it work. what is going on here?
The LESS file cannot read the actual class applied to the html body element at run time (you would probably need to implement a javascript solution to do something like that).
If you just want to have all themed css ready for use based on the body class, the best way to implement this to have all the necessary theme based css in a mixin, then apply it under the theme classes. This reduces code duplication. For example:
LESS
//mixin with all css that depends on your color
.mainThemeDependentCss() {
#contrast: lighten(#themeColor, 20%);
h1 {color: #themeColor;}
p {background-color: #contrast;}
}
//use the mixin in the themes
body.themeBlue {
#themeColor: blue;
.mainThemeDependentCss();
}
body.themeRed {
#themeColor: red;
.mainThemeDependentCss();
}
CSS Output
body.themeBlue h1 {
color: #0000ff;
}
body.themeBlue p {
background-color: #6666ff;
}
body.themeRed h1 {
color: #ff0000;
}
body.themeRed p {
background-color: #ff6666;
}
For some other answers that deal with aspects or ways of theming, see:
LESS CSS - Change variable value for theme colors depending on body class
LESS.css variable depending on class
LESS CSS: abusing the & Operator when nesting?
Variables in Less are actually constants and will only be defined once.
Scope works within its code braces, so you would need to nest your CSS within each theme you want (which means duplication).
This is not ideal as you would need to do this:
body.themeBlue {
#color: blue;
/* your css here */
}
body.themeRed {
#color: red;
/* your css here AGAIN :( */
}
You could, however, try to use variables like this:
#color: black;
#colorRed: red;
#colorBlue: blue;
h1 {
color: #color; // black
body.themeRed & {
color: #colorRed; // red
}
body.themeBlue & {
color: #colorBlue; // blue
}
}
You would only need to declare the colours once, but you would need to constantly do the "body.themeRed" etc. prefixes where the colour varies depending on the theme.
You could actually use #import to load your theme! So common.less would contain all your default styles and #themeColor will be applied to it.
.mainThemeDependentCss() {
//file with all themed styles
#import 'common.less';
}
//use the mixin in the themes
body.themeBlue {
#themeColor: blue;
.mainThemeDependentCss();
}
body.themeRed {
#themeColor: red;
.mainThemeDependentCss();
}
BTW you should avoid using body selector in your common.less, because it wouldn't work.

Creating or referencing variables dynamically in Sass

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