Is there a generic way to add vendor prefixes in LESS? - less

I currently have a mixins.less file, where almost all mixins are basically the same:
.border-radius(#radius) {
-webkit-border-radius: #radius;
-khtml-border-radius: #radius;
-moz-border-radius: #radius;
border-radius: #radius;
}
.box-shadow(#value) {
-webkit-box-shadow: #value;
-khtml-box-shadow: #value;
-moz-box-shadow: #value;
box-shadow: #value;
}
Is there a way to create some kind of generic mixin, that I could call like this:
.vendor('border-radius', '3px');
.vendor('box-shadox', '10px 10px');
and which would produce the same result as above?

Notice:
The recommendation is to stop rely on this technique and consider using a dedicated prefixing tool (e.g. Autoprefixer, -prefix-free etc.). Hardcoding vendor prefixes via CSS pre-processor mixins (Less, SCSS or whatever) is a pure anti-pattern these days and considered harmful. Auto-prefixing tools will make your code clean, readable, future-proof and easily maintainable/customizable.
See for example: less-plugin-autoprefix
Original answer:
Well, currently LESS does not support "property name interpolation" so you cannot use a variable in property names. There's a hack however: How to pass a property name as an argument to a mixin in less
So if you don't mind "dummy" properties in the output CSS, here we go:
.property_(#property, #value) {
_: ~"; #{property}:" #value;
}
.vendor(#property, #value) {
.property_('-webkit-#{property}', #value);
.property_( '-khtml-#{property}', #value);
.property_( '-moz-#{property}', #value);
.property_( #property, #value);
}
#usage {
.vendor(border-radius, 3px);
.vendor(box-shadow, 10px 10px);
}
Output:
#usage {
_: ; -webkit-border-radius: 3px;
_: ; -khtml-border-radius: 3px;
_: ; -moz-border-radius: 3px;
_: ; border-radius: 3px;
_: ; -webkit-box-shadow: 10px 10px;
_: ; -khtml-box-shadow: 10px 10px;
_: ; -moz-box-shadow: 10px 10px;
_: ; box-shadow: 10px 10px;
}
Update:
Less v1.6.0 introduced Property Interpolation feature so now you don't need any hacks anymore:
.vendor(#property, #value) {
-webkit-#{property}: #value;
-khtml-#{property}: #value;
-moz-#{property}: #value;
#{property}: #value;
}
#usage {
.vendor(border-radius, 3px);
.vendor(box-shadow, 10px 10px);
}

Related

how to use "sass:meta" in sass-loader on vue component

I have the next error on my component
SassError: Invalid CSS after "... $color in meta": expected expression (e.g. 1px, bold), was ".keywords($args) {"
on line 5 of src/assets/scss/components/_dMetricCircle.scss
from line 101 of src/components/UI/DMetricCircle.vue
#each $name, $color in meta.keywords($args) {
and this is my _dMetricCircle.scss
#use "sass:meta";
#mixin test-mixin($args...){
#each $name, $color in meta.keywords($args) {
.#{$name} {
background-color: map-get($color, "bgcolor");
border: 1px solid map-get($color, "border");
#if map-has-key($color, "pcolor") {
&.percent{
background: linear-gradient(var(--v), map-get($color, "pcolor") 50%, transparent 0) center/calc(var(--s) * 100%) border-box,
linear-gradient(var(--v), map-get($color, "bgcolor") 50%, transparent 0) center/calc(100% - var(--s) * 100%) border-box,
linear-gradient(to right, map-get($color, "pcolor") 50%, map-get($color, "bgcolor") 0) border-box;
}
}
}
}
}
and on my DMetricCircle component, I import and use the mixin like this
#include test-mixin(
$default: ("bgcolor": $gray-lighten-45, "border": $gray-lighten-30),
$success: ("bgcolor": $success-lighten-50, "border": $success-lighten-20, "pcolor": $gray-lighten-45),)
So, I don't know if sass-loader doesn't supper the use of meta or I'm doing a wrong use of sass compiler
"node-sass": "4.14.1","sass-loader": "10.0.2"

how to addition variable in less

I have the following =>
#header-height: 40;
#footer-height: 20;
I would like to be able to do
min-height: calc(~'100% - '#header-height+#footer-height'px') !important;
which return me
100%-60px
but my test fail as I get
min-height: calc(100% - 40+20 'px') !important;
You can use ${var} syntax to insert variable into less string expression:
min-height: ~'calc(100% - #{header-height}px - #{footer-height}px)' !important;
Moreover, it is better to execute the whole calc(...) command due ~'calc(...)' syntax

LESS loop to create modular scale

I am trying to realize a modular approach to text sizing using the following starting variables:
#font-size: 1.7rem;
#line-height: 1.414;
I would like to write a mixin that would create this result but have not quite fully grasped LESS yet:
h4 {
font-size: #font-size * #line-height;
}
h3 {
font-size: (#font-size * #line-height) * #line-height;
}
h2 {
font-size: ((#font-size * #line-height) * #line-height) * #line-height;
}
h1 {
font-size: (((#font-size * #line-height) * #line-height) * #line-height) * #line-height;
}
You have an array with your selectors - #elements.
Then you get the length of #elements - it will be used as iterator through an array.
Then the loop starts. It has the name set-font-size. In Less loops are recursive mixins. This mixin takes some parameters and calls itself until some condition is correct. In this example, it works while #_i > 0. Every time it calls itself, the value of #_i decreases.
Inside mixin (or loop) you get current element from array by calling extract(#array, #index) command. Then you set the font size to your selector. Evry time mixin call itself, the value of font-size is increased #_font-size * #_line-height.
That's all :)
Codepen DEMO.
Less:
#elements: h1, h2, h3, h4;
#iterations: length(#elements); // lehgth of #elements
#font-size: 1.7rem;
#line-height: 1.414;
.set-font-size(#_i, #_elements, #_font-size, #_line-height) when (#_i > 0) {
#selector: extract(#_elements, #_i);
#{selector} {
font-size: #_font-size;
}
.set-font-size(#_i - 1, #_elements, #_font-size * #_line-height, #_line-height);
}
.set-font-size(#iterations, #elements, #font-size, #line-height);
Css output:
h4 {
font-size: 1.7rem;
}
h3 {
font-size: 2.4038rem;
}
h2 {
font-size: 3.3989732rem;
}
h1 {
font-size: 4.8061481rem;
}

Passing a variable list through .for loop using Less 2.6.1 is unable to compile

I have been using a Less mixin which makes looping in Less, less verbose and is really handy: https://github.com/seven-phases-max/less.curious/
I upgraded from Less 2.6.0 to Less 2.6.1 and now the Less will not compile.
Here's the Less code:
// ............................................................
// .for
.for(#i, #n) {.-each(#i)}
.for(#n) when (isnumber(#n)) {.for(1, #n)}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
// ............................................................
// .for-each
.for(#array) when (default()) {.for-impl_(length(#array))}
.for-impl_(#i) when (#i > 1) {.for-impl_((#i - 1))}
.for-impl_(#i) when (#i > 0) {.-each(extract(#array, #i))}
#gray-base: #000;
#gray-darker: #222;
#gray-dark: #333;
#gray: #555;
#gray-light: #777;
#gray-lighter: #eee;
#gray-type:
gray,
gray-base,
gray-dark,
gray-darker,
gray-light,
gray-lighter;
.gray-type {
// Mixin to generate the gray font colours for p tags in the font stacks.
.for(#gray-type); .-each(#name) {
.#{name} {
color: ##name;
}
}
}
Here's the expected output:
.gray-type .gray {
color: #555;
}
.gray-type .gray-base {
color: #000;
}
.gray-type .gray-dark {
color: #333;
}
.gray-type .gray-darker {
color: #222;
}
.gray-type .gray-light {
color: #777;
}
.gray-type .gray-lighter {
color: #eee;
}
After upgrading to Less 2.6.1 I get the following error:
>> SyntaxError: variable #[object Object],[object Object] is undefined in less/style.less on line 41, column 3:
>> 40 // Mixin to generate the gray font colours for p tags in the font stacks.
>> 41 .for(#gray-type); .-each(#name) {
>> 42 .#{name} {
Warning: Error compiling less/style.less Use --force to continue.
I think the problem is related to the hyphens being stripped from the variables during compilation, therefore making the variables passed though the list invalid.
Any help would be greatly appreciated.

Smart margin mixin for LESS

I've created a .smartMargin() mixin for LESS CSS to be used in responsive design LESS.
I'm sharing it here for the benefit of others and also because I'm curious if there's any way to make it more efficient.
Idea
The mixin is designed to be called from within another mixin that represents a responsive design breakpoint. The idea is that you may want to override top, left, bottom, right margins individually by overriding one or more margins in each successive breakpoint.
In main mixin I just want one parameter for #videoMargin as opposed to #videoMarginLeft, #videoMarginRight etc so that's why I've called it 'smartMargin'.
Usage
In my main file I define a breakpoint like this, and then call this mixin several times:
.breakpoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
.smartMargin(#videoMargin);
}
}
.breakPoint(10em, red, 3em, 1em auto 1em auto);
.breakPoint(10em, green, 3em, 2em unset unset unset);
.breakPoint(20em, blue, 3em, unset 3em unset unset);
Output css
So for a given value of #videoMargin here's the output css generated
generated css
-------------
.smartMargin(3em); margin: 3em;
.smartMargin(3em 1em 2em 4em); margin: 3em 1em 2em 4em;
.smartMargin(3em unset unset unset); margin-top: 3em;
.smartMargin(3em unset unset 4em); margin-top: 3em;
margin-right: 3em;
Implementation
The mixin is as follows. It works well but it just seems a little clumsy in places and you need to provide either 4 or 1 parameters. If anybody can optimize this I'd be very interested to see.
.smartMargin(#margin) when (length(#margin) = 4)
{
._smartMargin() when (extract(#margin, 4) = unset), (extract(#margin, 3) = unset), (extract(#margin, 2) = unset), (extract(#margin, 1) = unset)
{
.marginComponent(#name, #value)
{
& when not (#value = unset)
{
#{name}: #value;
}
}
.marginComponent(margin-top, extract(#margin, 1));
.marginComponent(margin-right, extract(#margin, 2));
.marginComponent(margin-bottom, extract(#margin, 3));
.marginComponent(margin-left, extract(#margin, 4));
}
._smartMargin() when (default())
{
margin: #margin;
}
._smartMargin();
}
.smartMargin(#margin) when (default())
{
& when not (#margin = ~'') and not (#margin = unset)
{
margin: #margin;
}
}
You could possibly rewrite it to something like:
.smartMargin(#margin) when (isem(#margin)),(isem(extract(#margin,1))) and (isem(extract(#margin,2))) and (isem(extract(#margin,3))) and (isem(extract(#margin,4))) {
margin: #margin;
}
.smartMargin(#margin) when (default()) and (4 = length(#margin)) {
#positions: top, right, bottom, left;
.setmargin(#position,#margin) when (isem(#margin)){
margin-#{position}: #margin;
}
.setmargins(#i:1) when (#i <= 4){
.setmargin(extract(#positions,#i);extract(#margin,#i));
.setmargins((#i + 1));
}
.setmargins();
}
But in the first place i don't think there is something wrong with your code. Personally is should consider the use of unset. I think you should use the initial keyword or even 0 in stead of unset. This enables you to do the following:
.smartMargin(#margin){
margin: #margin;
}
.one{
.smartMargin(3em);
}
.two{
.smartMargin(3em 1em 2em 4em);
}
.three{
.smartMargin(3em 0 0 0);
}
.four{
.smartMargin(3em 0 0 4em);
}
Or consider to use Passing Rulesets to Mixins, than you can use something like that shown below:
.breakPoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
#videoMargin();
}
}
.breakPoint(10em, red, 3em, {margin: 1em auto 1em auto;});
.breakPoint(10em, red, 3em, {margin: 1em auto;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0 0;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0;});
.breakPoint(10em, green, 3em, {margin-top: 2em;});
.breakPoint(20em, blue, 3em, {margin: 0 3em 0 0;});
.breakPoint(20em, blue, 3em, {margin-right: 3em;});