Converting a SASS If/Else block to its equivalent in Less - less

There is a little SASS to LESS convergence here... Does anyone know what is the correct syntax for these? The code below is the pure SASS mixins I used to use. Thanks for helping
#mixin linear-gradient($left, $right, $optional:false) {
#if $optional and type_of($optional) == number {
background: -webkit-linear-gradient($optional + deg, $left, $right);
background: -o-linear-gradient($optional + deg, $left, $right);
background: -moz-linear-gradient($optional + deg, $left, $right);
background: linear-gradient($optional + deg, $left, $right);
} #else {
#if $optional == "right" {
background: -webkit-linear-gradient(left, $left, $right);
background: -o-linear-gradient(left, $left, $right);
background: -moz-linear-gradient(left, $left, $right);
background: linear-gradient(to right, $left, $right);
} #if $optional == "left" {
background: -webkit-linear-gradient(right, $left, $right);
background: -o-linear-gradient(right, $left, $right);
background: -moz-linear-gradient(right, $left, $right);
background: linear-gradient(to left, $left, $right);
} #else { // top to bottom
background: -webkit-linear-gradient($right, $left);
background: -o-linear-gradient($right, $left);
background: -moz-linear-gradient($right, $left);
background: linear-gradient($right, $left);
}
}
}

Less uses guarded mixins with when conditions to mimick the if/else logic. You can convert that SASS mixin to its Less equivalent like shown below. Most of the code is self explanatory (provided you have basic understanding of how Less works). I have also added some comments inline to make it clearer.
.linear-gradient(#left, #right, #optional:false) {
& when (isnumber(#optional)) { //just isnumber check should be enough because default value is also not a number
background: -webkit-linear-gradient(~"#{optional}deg", #left, #right);
/* "#{optional}deg" is used for string concatenation, ~ outputs the value w/o quotes */
background: -o-linear-gradient(~"#{optional}deg", #left, #right);
background: -moz-linear-gradient(~"#{optional}deg", #left, #right);
background: linear-gradient(~"#{optional}deg", #left, #right);
}
& when not (isnumber(#optional)) { //else part
& when (#optional = right) { //if value of optional param is right
background: -webkit-linear-gradient(left, #left, #right);
background: -o-linear-gradient(left, #left, #right);
background: -moz-linear-gradient(left, #left, #right);
background: linear-gradient(to right, #left, #right);
}
& when (#optional = left) { //else if value of optional param is left
background: -webkit-linear-gradient(right, #left, #right);
background: -o-linear-gradient(right, #left, #right);
background: -moz-linear-gradient(right, #left, #right);
background: linear-gradient(to left, #left, #right);
}
& when (#optional = false) { // else if the value is the default value
background: -webkit-linear-gradient(#right, #left);
background: -o-linear-gradient(#right, #left);
background: -moz-linear-gradient(#right, #left);
background: linear-gradient(#right, #left);
}
}
}
and invoke it like (ignore the values for first two params, just dummy)
div#div1{
.linear-gradient(10px, 10px, 10);
}
div#div2{
.linear-gradient(10px, 10px, right);
}
div#div3{
.linear-gradient(10px, 10px, left);
}
div#div4{
.linear-gradient(10px, 10px);
}
The compiled output would be
div#div1 {
background: -webkit-linear-gradient(10deg, 10px, 10px);
background: -o-linear-gradient(10deg, 10px, 10px);
background: -moz-linear-gradient(10deg, 10px, 10px);
background: linear-gradient(10deg, 10px, 10px);
}
div#div2 {
background: -webkit-linear-gradient(left, 10px, 10px);
background: -o-linear-gradient(left, 10px, 10px);
background: -moz-linear-gradient(left, 10px, 10px);
background: linear-gradient(to right, 10px, 10px);
}
div#div3 {
background: -webkit-linear-gradient(right, 10px, 10px);
background: -o-linear-gradient(right, 10px, 10px);
background: -moz-linear-gradient(right, 10px, 10px);
background: linear-gradient(to left, 10px, 10px);
}
div#div4 {
background: -webkit-linear-gradient(10px, 10px);
background: -o-linear-gradient(10px, 10px);
background: -moz-linear-gradient(10px, 10px);
background: linear-gradient(10px, 10px);
}
Note: As mentioned in comments, it is always better to use the built in unit function or the math operation to convert a plain number into degrees (or anything like px, em etc) instead of using the string concatenation method. The following are samples on how to do it.
Using unit function:
background: -webkit-linear-gradient(unit(#optional,deg), #left, #right);
Using math operation:
background: -webkit-linear-gradient(#optional * 1deg, #left, #right);

Related

Generate rules via LESS plugin

I'd like to write a plugin that can generate a LESS function named alt that can do the following transformation:
.button {
background-color: alt(red, blue);
color: alt(black, white);
}
And output the following:
.button {
background-color: red;
color: black;
body.alt & {
background-color: blue;
color: white;
}
}
There doesn't seem to be much documentation about writing LESS plugins on the site, so hoping someone can provide an example of how this might be written :) Thanks!
I think a mixin would be best for this. Something like the following LESS:
.alt(#property, #primary-color, #alternate-color) {
#{property}: #primary-color;
body.alt & {
#{property}: #alternate-color;
}
}
.button {
.alt(background-color, red, blue);
.alt(color, black, white);
}
Which will compile to the following CSS:
.button {
background-color: red;
color: black;
}
body.alt .button {
background-color: blue;
}
body.alt .button {
color: white;
}

Making this LESS Mixin more efficient and less redundant?

so for a website project I had to create a LESS Mixin which I had never done before. I have the feeling that it is not as efficient as it could be and may be a little redundant. I looked through it but couldnt think of a better way to do it. Maybe you can give me some advice? Dont get me wrong, its working perfectly fine but as mentioned I guess I could do it a little easier when it comes to the LESS code.
So here it is:
http://jsfiddle.net/T2Xe9/747/
Example HTML:
<style media="screen">
div {
background: crimson;
width: 200px;
height: 100px;
position: relative;
float: left;
margin-right: 20px;
}
</style>
<div class="example-1"></div>
<div class="example-2"></div>
<div class="example-3"></div>
<div class="example-4"></div>
Mixin with examples:
.clipping(#colorOne; #colorTwo; #width; #direction; #position; #side) {
display: inline-block;
width: #width;
height: 20%;
position: absolute;
content: " ";
& when (#direction = 'up') {
background: linear-gradient(to top left, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
}
& when (#direction = 'down') {
background: linear-gradient(to top right, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
}
& when (#position = top) {
top: 0;
}
& when (#position = bottom) {
bottom: 0;
}
& when (#side = 'left') {
left: 0;
}
& when (#side = 'right') {
right: 0;
}
}
.clipping-single(#colorOne; #colorTwo; #direction; #position) {
&:before {
.clipping(#colorOne; #colorTwo; 100%; #direction; #position; 'left');
}
}
.clipping-double(#colorOne; #colorTwo; #direction; #position) {
& when (#direction = 'up') {
&:before {
.clipping(#colorOne; #colorTwo; 50%; 'up'; #position; 'left');
}
&:after {
.clipping(#colorOne; #colorTwo; 50%; 'down'; #position; 'right');
}
}
& when (#direction = 'down') {
&:before {
.clipping(#colorOne; #colorTwo; 50%; 'down'; #position; 'left');
}
&:after {
.clipping(#colorOne; #colorTwo; 50%; 'up'; #position; 'right');
}
}
}
.example-1 {
.clipping-double(crimson, #fff, 'up', top);
}
.example-2 {
.clipping-double(#fff, crimson, 'down', bottom);
}
.example-3 {
.clipping-single(#fff, crimson, 'down', bottom);
}
.example-4 {
.clipping-single(crimson, #fff, 'up', top);
}
I have two suggestions for you. Both will reduce the redundancy and produce the same CSS as the original version. If more efficient means less lines of SASS code then this is also the case.
& when (#position = top) {
top: 0;
}
& when (#position = bottom) {
bottom: 0;
}
can be replace with
#{position}: 0;
and the same for #{side}: 0; but you would have to pass left instead of 'left' as an argument.
The second suggestion is to refactor the line where you set the linear-gradient. The only variable is the direction so I would make this a SASS variable to avoid the redundancy of the other linear-gradient parameters.
& when (#direction = 'up') {
background: linear-gradient(to top left, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
}
& when (#direction = 'down') {
background: linear-gradient(to top right, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
}
becomes
.define-gradient-direction(#direction);
.define-gradient-direction('up') { #gradientDir: to top left; }
.define-gradient-direction('down') { #gradientDir: to top right; }
background: linear-gradient(#gradientDir, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
Full code
.clipping(#colorOne; #colorTwo; #width; #direction; #position; #side) {
.define-gradient-direction(#direction);
.define-gradient-direction('up') { #gradientDir: to top left; }
.define-gradient-direction('down') { #gradientDir: to top right; }
display: inline-block;
width: #width;
height: 20%;
position: absolute;
content: " ";
background: linear-gradient(#gradientDir, #colorOne 0%, #colorOne 50%, #colorTwo 50%, #colorTwo 100%);
#{position}: 0;
#{side}: 0;
}
.clipping-single(#colorOne; #colorTwo; #direction; #position) {
&:before {
.clipping(#colorOne; #colorTwo; 100%; #direction; #position; left);
}
}
.clipping-double(#colorOne; #colorTwo; #direction; #position) {
& when (#direction = 'up') {
&:before {
.clipping(#colorOne; #colorTwo; 50%; 'up'; #position; left);
}
&:after {
.clipping(#colorOne; #colorTwo; 50%; 'down'; #position; right);
}
}
& when (#direction = 'down') {
&:before {
.clipping(#colorOne; #colorTwo; 50%; 'down'; #position; left);
}
&:after {
.clipping(#colorOne; #colorTwo; 50%; 'up'; #position; right);
}
}
}
.example-1 {
.clipping-double(crimson, #fff, 'up', top);
}
.example-2 {
.clipping-double(#fff, crimson, 'down', bottom);
}
.example-3 {
.clipping-single(#fff, crimson, 'down', bottom);
}
.example-4 {
.clipping-single(crimson, #fff, 'up', top);
}
Edit: thanks to seven-phases-max for pointing out an improvement in the comments. It's now part of the snippet.

What is wrong with these SCSS media queries [duplicate]

This question already has answers here:
Using Sass Variables with CSS3 Media Queries
(8 answers)
Closed 7 years ago.
I am using the official Sass port of Twitter Bootstrap 3.3.3.
What I am trying to do is shrink the height of the navbar when the window is resized. Below is my media queries, however they don't work as I expect them to.
$navbar-height: 60px !default;
body {
padding-top: 70px !important;
}
#media(min-width: 768px) {
$navbar-height: 70px;
body {
padding-top: 80px !important;
}
}
#media(min-width: 992px) {
$navbar-height: 80px;
body {
padding-top: 90px !important;
}
}
#media(min-width: 1200px) {
$navbar-height: 90px;
body {
padding-top: 100px !important;
}
}
To make it work modify the element inside the #media query not the variable. So for example...
$navbar-height: 60px !default;
body {
padding-top: 70px !important;
}
#media(min-width: 768px) {
.nav-bar: $navbar-height + 10px;
body {
padding-top: 80px !important;
}
}
#media(min-width: 992px) {
.nav-bar: $navbar-height + 20px;
body {
padding-top: 90px !important;
}
}
#media(min-width: 1200px) {
.nav-bar: $navbar-height + 30px;
body {
padding-top: 100px !important;
}
}

Splitting up a list of variables in Sass

In Sass is there a way to split up a list of variables / classes with hyphens?
It's a fuzzy question title so it's probably best I show what I'm trying to achieve.
In the below example I'm trying to create some utility classes that I can apply to HTML elements to help with vertical rhythm.
For example I may want to give an element a small margin that is consistent with my vertical rhythm strategy and so I'll add the class .m-t-s (which stands for margin-top-small).
I then want to output versions of those utility classes against for each media query I have for fine grain control e.g. I may want a class .m-t-s-768 which will add a small top margin when there is a minimum viewport width of 768px.
I have achieved this below, but it is a very long-winded and repetitive way of doing it. Can anyone suggest a more concise way?
Variables
––––––––––
$mediaQueries-px:
640,
768,
1024
;
$s: 20px; /* FYI I've simplified these examples for the sake of demonstration, normally I use something like ($baseLineHeight / 1.5) + rem */
$m: 50px;
$l: 60px;
Creating the classes
–––––––––––––––––––––
.m-t-s {
margin-top: $s;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-s-#{$mediaQuery} {
margin-top: $s;
}
}
}
.m-t-m {
margin-top: $m;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-m-#{$mediaQuery} {
margin-top: $m;
}
}
}
This repeats for .m-t-l too (margin top large), and then it continues for padding classes (e.g. .p-t-s and so on), so it gets to be a pretty long list of utility classes.
To programatically generate that output, you need another list and an inner loop:
$mediaQueries-px:
640,
768,
1024
;
$s: 20px;
$m: 50px;
$l: 60px;
$sizes: s $s, m $m, l $l;
#each $size in $sizes {
.m-t-#{nth($size, 1)} {
margin-top: nth($size, 2);
}
}
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16 * 1em)) { // modified for compilation purposes
#each $size in $sizes {
.m-t-#{nth($size, 1)}-#{$mediaQuery} {
margin-top: nth($size, 2);
}
}
}
}
Output:
.m-t-s {
margin-top: 20px;
}
.m-t-m {
margin-top: 50px;
}
.m-t-l {
margin-top: 60px;
}
#media screen and (min-width: 40em) {
.m-t-s-640 {
margin-top: 20px;
}
.m-t-m-640 {
margin-top: 50px;
}
.m-t-l-640 {
margin-top: 60px;
}
}
#media screen and (min-width: 48em) {
.m-t-s-768 {
margin-top: 20px;
}
.m-t-m-768 {
margin-top: 50px;
}
.m-t-l-768 {
margin-top: 60px;
}
}
#media screen and (min-width: 64em) {
.m-t-s-1024 {
margin-top: 20px;
}
.m-t-m-1024 {
margin-top: 50px;
}
.m-t-l-1024 {
margin-top: 60px;
}
}

How to apply concatenated classes within another class using Less?

Let's say I have the following Less setup:
.box {
border: 1px solid #333;
&.error {
background-color: Red;
}
}
If I wanted to declare another class which applied the full style of .box.error, as .error-box for example, what's the correct syntax?
If I use:
.error-box {
.box.error;
}
All I get is the red background, with no border. I've tried many different combinations, but I always get a syntax error.
I plugged in your less as so:
.box {
border: 1px solid #333;
&.error {
background-color:red;
}
}
.error-box {
.box;
}
and the CSS output was this:
.box {
border: 1px solid #333;
}
.box.error {
background-color: red;
}
.error-box {
border: 1px solid #333;
}
.error-box.error {
background-color: red;
}
were you wanting the .error-box class to alone receive both styles? The only way I can think of doing that would be:
.error-bg {
background:red;
}
.box {
border:1px solid #333;
&.error {
.error-bg;
}
}
.error-box {
.box;
.error-bg;
}