Using CSS custom properties (variables) with LESS functions - css

In my LESS file, I have this:
:root{
--base-color: red;
}
In my project --base-color may change "on the fly" from a user input so every instance of red may become for example green.
The problem is I have some LESS functions for applying tint, shadow or saturations so I'm trying to do something like this:
.tint{
color: tint(var(--base-color), 80%);
}
But I receive this error:
Error: error evaluating function tint: color2.toHSL is not a
function
Obviously I can't store --base-color in a less variables because I would lose the instance of the variable, so color: tint(#base-color, 80%) is not an acceptable answer.
Is there a way to keep the instance of --base-color in my css minified field?
Thanks.

Related

CSS variables and SASS functions

We know the advantages of using CSS4 variables but what if we need to get these values from a SASS function like so?:
:root {
--gap-l: toRem(10);
}
toRem is a Sass function that I call to get the sizes dynamically:
$fontSize: 16;
#function toRem($val) {
#return $val / $fontSize * 1.6 + 0rem;
}
This won't fail but won't work either. To have this working we can just have the value directly on --gap-l or keep using SASS vars.
If I try something like --gap-l: #{toRem(10)}; this is the error I get:
It doesn't call the SASS function
You can definitely do that: what you're missing is simply using string interpolation, i.e.:
:root {
--gap-l: #{toRem(10)};
}
The reason is highlighted in their "breaking changes" documentation with regards to CSS variables:
To provide maximum compatibility with plain CSS, more recent versions of Sass require SassScript expressions in custom property values to be written within interpolation. Interpolation will also work for older Sass versions, and so is recommended for all stylesheets.
Try this --gap-l: #{toRem(10)};, the #{} syntax is called interpolation. In the experience of heading bugs myself, when you want to use a SASS expression's raw value along with normal CSS, if you can't use the concise syntax like just toRem(10), try adding interpolation to see if it works.
Another example:
$padding: 5px;
.element {
width: calc(100% - $padding); // will not work, it must be:
width: calc(100% - #{$padding});
}
Here is the code with the updated question: https://jsfiddle.net/bcw763en/.
Notice that if you put :root above the #function, it'll not work.

How can I create unique variables when using mixins as functions in LESS?

I needed to create a function for some big color work on my Bootstrap variables. Unfortunately LESS doesn't allow you to create functions that can be called like theirs (ex. #myvar: darken(#color, 20%);).
The option provided on the doc site is to use a mixin that returns a variable. This worked well for me when I used it where the variable was declared as the property value, but I need to run my new mixin on many variables in the Bootstrap variable.less file. If I call the mixin multiple times there, it always returns the first color.
Part that works:
.mixin(#color) {
#var: #color;
}
.caller-1 {
.mixin(blue);
color:#var;
}
.caller-2 {
.mixin(red);
color:#var;
}
CSS
.caller-1 {
color:blue;
}
.caller-2 {
color:red;
}
What does not work:
.mixin(blue);
#color-1: #var; // My value is now blue
.mixin(red);
#color-2: #var; // My value is also blue
I thought I could get around this by building a unique variable in the mixin, but I can't find anyway to build one.
.mixin(#color; #num)
#var+#{num}: #color;
}
.mixin(blue; 1);
#color-1: #var1;
.mixin(red; 2);
#color-2: #var2;
Any idea on how to create a variable name in a mixin or other ideas on how to make one work like the LESS functions?
You can't define variables dynamically in LESS right now, but you can dynamically define selectors (as you probably knew). I will just give an example of that and leave it to you to apply it to the color/variables issue.
.towerMaker (#index) when (#index > 0) {
.block-#{index} {
z-index: #{index};
}
.towerMaker(#index - 1);
}
.towerMaker (7);
Variables are actually constants, and their scope is based only on context (where they appear in the block doesn't matter). It's only different when you call it within a selector block because of context. When you call the mixin at top-level, you define #var once for that level and it won't be overriden.
If you have to use variables, I suggest you try to find a solution taking advantage of the context. For example, you might be able to try something with mixin guards & when(condition) {...} (it's actually even simpler, as #seven-phases-max commented below). This is a way to run a mixin outside the context of a selector but still inside a context (updated example):
& {
.mixin(red);
.test1 { color: #var; }
}
& {
.mixin(blue);
.test1 { color: #var; }
}
You actually can define functions that will be called using Less by your Less runtime, but they can't be defined using Less. This is possible if you run your processor using Node.js, for example. But it's quite a hack and not trivial since you have to write them in JavaScript and wrap values in undocumented less.js types.
You can also call core JavaScript enclosing it within backticks (this is also undocumented). It's good for small blocks of code and for core functions:
length: unit(`Math.log(#{value})`, px);
If you run your Less processor from a Node.js app you can call your own functions that way.

Less CSS: driving includes through parameters

We have a LESS based stylesheet for which we would like to generate multiple color variations. We have defined an include file which contains the color variations (for now blue.less) and would like to generate and use green and red variations of this include file.
What we would like to do is to somehow parametrize less via the command line to include one of these specific color files. Does anybody know how this can be done?
Put the color definitions into variables in a separate file, eg. blue.less:
#baseColor: #0000ff; // blue
#accentColor: #00ffff; // yellow?
then create a separate theme file, eg. theme.less, where you define how the base color should be changed to achieve your theme:
#link-color: #baseColor;
#link-hover-color: darken(#link-color, 15%);
#panel-header-highlight-color: darken(#accentColor, 15%);
...
then in a third file, e.g. layout.less, you define
a {
color: #link-color;
&:hover {
color: #link-hover-color;
}
}
...
finally, create your blue-master.less:
#import "blue.less";
#import "theme.less";
#import "layout.less";
To create a red-master.less you should only need to write a color file and change one line in the above.
You can call the color-file color.less, and e.g. have a blue/color.less and a red/color.less, and use the --include-path=.. parameter to lessc to make it generate different files based on command line parameters, but I would advise against it (it becomes much more arduous to work with in my experience).

LESS.js + #arguments + Skip One Argument

I have been searching in Google etc., but I couldnt find what I was looking for (I hope I didnt overlook something).. So I thought my best bet is to ask you guys :)
I am playing around with LESS-JS for the first time and I really like it. However I have a little problem now.
I am using the #arguments variable like this:
.basicBorder(#width:1px, #type:solid, #color:#black){
border:#arguments;
}
Which works as expected. Now when I want the border to be red, I am adding this to the element in my css:
.basicBorder(1px, solid, #red);
Which also works as expected. However I would like to avoid writing 1px, solid,, since these are my default values already, but when I try this:
.basicBorder(#red);
Or this:
.basicBorder(,,#red);
It doesnt work.
So I was wondering if any1 knows how I could "skip" the first 2 variables so that I can just input the color in case I dont want the border-width and type to be changed.
I hope you get what I am trying to say!
Regards!
You actually can name later parameters and skip the first ones. The syntax for your question is:
.basicBorder(#color:#red);
You can also use normal ordered arguments at the beginning and pluck out named arguments from the rest of the parameters:
.basicBorder(2px, #color:#red);
This sets #width to 2px, #type to the default, and #color to #red. Really nice if you have more seldom used arguments.
The parametric mixins in LESS works sorta like javascript functions, you can't skip the first parameters. So if you want to only change the color, you could rewrite the mixin like this:
.basicBorder(#color:#black, #width:1px, #type:solid){
border:#width #type #color;
}
Then you'd be able to call it like this:
.basicBorder(#red);
.basicBorder(#red, 2px, dotted);
edit
Using your original mixin, you could also create these
.basicBorderType(#type) {
.basicBorder(1px, #type, #black);
}
.basicBorderColor(#color) {
.basicBorder(1px, solid, #color);
}
Now you could overwrite any of the styles:
.basicBorderType(dotted); //1px dotted black;
.basicBorderColor(#red); //1px solid red;
.basicBorder(2px); //2px solid black;
A bit of a hack, but it's the only thing I can think of to help you out...

SASS: Set variable at compile time

Is it possible to set a sass variable at compile time? I basically want to do this:
$color: red !default;
div#head {
background-color: $color;
}
When I compile to css I want to set $color to "blue" (preferably from the command line). Has anyone been able to do this?
Thanks,
Chris
I found this at their FAQ http://sass-lang.com/docs/yardoc/file.FAQ.html
If you just want to pass some variables to the CSS every time it gets compiled, like using --watch, you can use Sass functions to define Ruby scripts to even query a database. But the code is going to be compiled only once, and served statically.
But if you need to recompile it at every request with different options,
you can use Sass::Engine to render the code, using the :custom option
to pass in data that can be accessed from your Sass functions
Seems like it's not recommended, though. Probably for performance reasons.
An alternate of command line options is to create other files assigning values to variables.
Assume that your code above is in a file named 'style.scss'.
To set $color to "blue", create a file such as:
$color: blue;
#import "style";
and name it to 'blue.scss' for example.
Then compile it with below.
sass blue.scss:style.css
When you want to assign another value to the variable, make another file named "green.scss" like:
$color: green;
#import "style";
Then compile it with
sass green.scss:anotherstyle.css
It is bothering somewhat but enables to decide values of variables at compile time.

Resources